create-ai-project 1.23.1 → 1.23.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/.claude/agents-en/acceptance-test-generator.md +16 -1
- package/.claude/agents-en/code-reviewer.md +8 -0
- package/.claude/agents-en/document-reviewer.md +21 -1
- package/.claude/agents-en/integration-test-reviewer.md +11 -1
- package/.claude/agents-en/task-decomposer.md +10 -0
- package/.claude/agents-en/task-executor-frontend.md +7 -1
- package/.claude/agents-en/task-executor.md +7 -1
- package/.claude/agents-en/technical-designer-frontend.md +10 -48
- package/.claude/agents-en/technical-designer.md +10 -26
- package/.claude/agents-en/work-planner.md +6 -0
- package/.claude/agents-ja/acceptance-test-generator.md +17 -2
- package/.claude/agents-ja/code-reviewer.md +8 -0
- package/.claude/agents-ja/document-reviewer.md +21 -1
- package/.claude/agents-ja/integration-test-reviewer.md +11 -1
- package/.claude/agents-ja/task-decomposer.md +10 -0
- package/.claude/agents-ja/task-executor-frontend.md +7 -1
- package/.claude/agents-ja/task-executor.md +7 -1
- package/.claude/agents-ja/technical-designer-frontend.md +10 -48
- package/.claude/agents-ja/technical-designer.md +9 -25
- package/.claude/agents-ja/work-planner.md +6 -0
- package/.claude/commands-en/front-build.md +14 -1
- package/.claude/commands-en/front-plan.md +15 -2
- package/.claude/commands-en/plan.md +15 -1
- package/.claude/commands-ja/front-build.md +14 -1
- package/.claude/commands-ja/front-plan.md +14 -1
- package/.claude/commands-ja/plan.md +15 -1
- package/.claude/skills-en/documentation-criteria/references/plan-template.md +20 -0
- package/.claude/skills-en/documentation-criteria/references/task-template.md +12 -0
- package/.claude/skills-en/frontend-technical-spec/SKILL.md +4 -4
- package/.claude/skills-en/frontend-typescript-rules/SKILL.md +45 -111
- package/.claude/skills-en/frontend-typescript-testing/SKILL.md +8 -6
- package/.claude/skills-en/subagents-orchestration-guide/SKILL.md +9 -7
- package/.claude/skills-en/task-analyzer/references/skills-index.yaml +9 -11
- package/.claude/skills-ja/documentation-criteria/references/plan-template.md +20 -0
- package/.claude/skills-ja/documentation-criteria/references/task-template.md +12 -0
- package/.claude/skills-ja/frontend-technical-spec/SKILL.md +3 -3
- package/.claude/skills-ja/frontend-typescript-rules/SKILL.md +43 -288
- package/.claude/skills-ja/frontend-typescript-testing/SKILL.md +15 -71
- package/.claude/skills-ja/subagents-orchestration-guide/SKILL.md +9 -7
- package/.claude/skills-ja/task-analyzer/references/skills-index.yaml +10 -11
- package/CHANGELOG.md +18 -0
- package/package.json +1 -1
|
@@ -5,311 +5,66 @@ description: React/TypeScriptの型安全性、コンポーネント設計、状
|
|
|
5
5
|
|
|
6
6
|
# TypeScript 開発ルール(フロントエンド)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
実装向けの frontend 固有 React/TypeScript ルール: しきい値、境界での型安全性、コンポーネント/状態の設計、エラーハンドリング、プロジェクト規約。
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
10
|
+
## アンチパターンとしきい値
|
|
11
|
+
設計変更を促すシグナル:
|
|
12
|
+
- prop drilling が 3 階層以上 → Context または状態管理へ持ち上げる
|
|
13
|
+
- コンポーネントが 300 行超 → 分割する
|
|
14
|
+
- Props が 10 個超 → コンポーネントを分割(3〜7 個が適正範囲)
|
|
15
|
+
- optional Props が 50% 超 → デフォルト値または Context を導入する
|
|
16
|
+
- Props のネストが 2 階層超 → フラット化する
|
|
17
|
+
- 同一の `as` アサーションが 3 回以上出現 → 型設計を見直す
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
function processData(data: unknown): User {
|
|
19
|
-
if (!isUser(data)) throw new Error('Invalid user data')
|
|
20
|
-
return data
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// 悪い: any型の使用
|
|
24
|
-
function processData(data: any): User {
|
|
25
|
-
return data as User
|
|
26
|
-
}
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
### 型定義のベストプラクティス
|
|
30
|
-
|
|
31
|
-
#### オブジェクト型
|
|
32
|
-
- **interface優先**: 拡張可能なオブジェクト型にはinterfaceを使用
|
|
33
|
-
- **typeはunion/intersection用**: 複合型やユーティリティ型に使用
|
|
34
|
-
- **readonlyの活用**: 不変なプロパティにはreadonlyを明示
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
37
|
-
// 良い: 明確な型定義
|
|
38
|
-
interface User {
|
|
39
|
-
readonly id: string
|
|
40
|
-
name: string
|
|
41
|
-
email: string
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
type UserWithRole = User & { role: 'admin' | 'user' }
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
#### 関数型
|
|
48
|
-
- **戻り値型を明示**: 複雑なロジックを持つ関数
|
|
49
|
-
- **ジェネリクスの活用**: 再利用可能な型安全な関数
|
|
50
|
-
|
|
51
|
-
```typescript
|
|
52
|
-
// 良い: 戻り値型を明示
|
|
53
|
-
function calculateTotal(items: CartItem[]): number {
|
|
54
|
-
return items.reduce((sum, item) => sum + item.price, 0)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// 良い: ジェネリクスの活用
|
|
58
|
-
function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
|
|
59
|
-
// implementation
|
|
60
|
-
}
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
## Reactコンポーネント設計
|
|
64
|
-
|
|
65
|
-
### Function Components必須
|
|
66
|
-
|
|
67
|
-
```typescript
|
|
68
|
-
// 良い: Function Component
|
|
69
|
-
const UserCard: React.FC<UserCardProps> = ({ user, onSelect }) => {
|
|
70
|
-
return (
|
|
71
|
-
<div onClick={() => onSelect(user.id)}>
|
|
72
|
-
{user.name}
|
|
73
|
-
</div>
|
|
74
|
-
)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// 悪い: Class Component(非推奨)
|
|
78
|
-
class UserCard extends React.Component<UserCardProps> { }
|
|
79
|
-
```
|
|
19
|
+
## 境界での型安全性
|
|
20
|
+
`any` を禁止する。型が得られない場合は `unknown` で受けて型ガードで絞り込む。`as` は最小化する(やむを得ない場合は理由をコメント)。
|
|
80
21
|
|
|
81
|
-
|
|
22
|
+
アプリ内部では React の Props/State は型保証されており `unknown` は不要。外部境界では必ず `unknown` で受け、使用前に型ガードで絞り込む: API レスポンス、`localStorage`/`sessionStorage`、URL パラメータ、パースした JSON。制御コンポーネントのフォーム入力は React 合成イベントを通じて型安全に保たれる。
|
|
82
23
|
|
|
83
24
|
```typescript
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
variant?: 'primary' | 'secondary'
|
|
88
|
-
disabled?: boolean
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const Button: React.FC<ButtonProps> = ({
|
|
92
|
-
label,
|
|
93
|
-
onClick,
|
|
94
|
-
variant = 'primary',
|
|
95
|
-
disabled = false
|
|
96
|
-
}) => {
|
|
97
|
-
// implementation
|
|
98
|
-
}
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### Children Props
|
|
102
|
-
|
|
103
|
-
```typescript
|
|
104
|
-
interface LayoutProps {
|
|
105
|
-
children: React.ReactNode
|
|
106
|
-
sidebar?: React.ReactNode
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const Layout: React.FC<LayoutProps> = ({ children, sidebar }) => (
|
|
110
|
-
<div>
|
|
111
|
-
<main>{children}</main>
|
|
112
|
-
{sidebar && <aside>{sidebar}</aside>}
|
|
113
|
-
</div>
|
|
114
|
-
)
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
## 状態管理
|
|
118
|
-
|
|
119
|
-
### useState型定義
|
|
120
|
-
|
|
121
|
-
```typescript
|
|
122
|
-
// 良い: 明示的な型
|
|
123
|
-
const [user, setUser] = useState<User | null>(null)
|
|
124
|
-
const [items, setItems] = useState<Item[]>([])
|
|
125
|
-
|
|
126
|
-
// 良い: 初期値から推論可能な場合
|
|
127
|
-
const [count, setCount] = useState(0)
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### useReducerの型安全性
|
|
131
|
-
|
|
132
|
-
```typescript
|
|
133
|
-
type Action =
|
|
134
|
-
| { type: 'SET_USER'; payload: User }
|
|
135
|
-
| { type: 'CLEAR_USER' }
|
|
136
|
-
| { type: 'SET_ERROR'; payload: string }
|
|
137
|
-
|
|
138
|
-
interface State {
|
|
139
|
-
user: User | null
|
|
140
|
-
error: string | null
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const reducer = (state: State, action: Action): State => {
|
|
144
|
-
switch (action.type) {
|
|
145
|
-
case 'SET_USER':
|
|
146
|
-
return { ...state, user: action.payload, error: null }
|
|
147
|
-
case 'CLEAR_USER':
|
|
148
|
-
return { ...state, user: null }
|
|
149
|
-
case 'SET_ERROR':
|
|
150
|
-
return { ...state, error: action.payload }
|
|
151
|
-
}
|
|
152
|
-
}
|
|
25
|
+
const raw: unknown = await (await fetch(url)).json()
|
|
26
|
+
if (!isUser(raw)) throw new ValidationError('invalid user')
|
|
27
|
+
const user = raw // User に絞り込み済み
|
|
153
28
|
```
|
|
154
29
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
refetch: () => Promise<void>
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function useUser(userId: string): UseUserReturn {
|
|
166
|
-
const [user, setUser] = useState<User | null>(null)
|
|
167
|
-
const [loading, setLoading] = useState(true)
|
|
168
|
-
const [error, setError] = useState<Error | null>(null)
|
|
169
|
-
|
|
170
|
-
const refetch = useCallback(async () => {
|
|
171
|
-
setLoading(true)
|
|
172
|
-
try {
|
|
173
|
-
const data = await fetchUser(userId)
|
|
174
|
-
setUser(data)
|
|
175
|
-
} catch (e) {
|
|
176
|
-
setError(e instanceof Error ? e : new Error('Unknown error'))
|
|
177
|
-
} finally {
|
|
178
|
-
setLoading(false)
|
|
179
|
-
}
|
|
180
|
-
}, [userId])
|
|
181
|
-
|
|
182
|
-
useEffect(() => { refetch() }, [refetch])
|
|
183
|
-
|
|
184
|
-
return { user, loading, error, refetch }
|
|
185
|
-
}
|
|
186
|
-
```
|
|
30
|
+
## コンポーネントと状態の設計
|
|
31
|
+
- **Function component のみ。** class component は Error Boundary に限り許可(hook の代替が存在しないため)。
|
|
32
|
+
- **Props は名前付き型で明示**し分割代入する: `function UserCard({ user, onSelect }: UserCardProps)`。`React.FC` は使わず、props を関数に直接型付けして Props 契約を明示する。
|
|
33
|
+
- **Props 駆動:** 依存は Props で渡す。グローバル状態や Context へは必要なときだけアクセスする。
|
|
34
|
+
- **Custom hook** をロジック再利用と依存注入の単位とする(テスト容易性のため、協調オブジェクトは hook 経由で注入する)。
|
|
35
|
+
- **関数引数:** 位置引数は 0〜2 個。3 個以上は単一の options オブジェクトで受ける。
|
|
36
|
+
- **状態の形:** 状態は明示的に型付けする。複数フィールドかつ離散的な遷移を持つ状態は、複数の `useState` ではなく discriminated union の action 型を用いた `useReducer` にする。
|
|
187
37
|
|
|
188
38
|
## エラーハンドリング
|
|
189
|
-
|
|
190
|
-
|
|
39
|
+
- すべてのエラーを表に出す: ログして処理するか伝播する — 握り潰さない。
|
|
40
|
+
- **Fail fast:** 不正な状態では、無言のフォールバックを返さず throw する。
|
|
41
|
+
- 想定内の失敗は `Result` 型で値として表現する。`throw` は想定外/回復不能なケースに限る。
|
|
42
|
+
- 目的別のエラークラスは `code` を持つ基底 `AppError` を継承する(例: ValidationError, ApiError, NotFoundError)。
|
|
43
|
+
- **層の責務:** API 層は transport エラーをドメインエラーへ変換する。hook は `AppError` を上位へ伝播する。Error Boundary はレンダリング時のエラーを捕捉しフォールバック UI を表示する。
|
|
44
|
+
- 機微情報(password, token, apiKey, creditCard)をログに出さない。
|
|
191
45
|
|
|
192
46
|
```typescript
|
|
193
|
-
|
|
194
|
-
children: React.ReactNode
|
|
195
|
-
fallback: React.ReactNode
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
interface ErrorBoundaryState {
|
|
199
|
-
hasError: boolean
|
|
200
|
-
}
|
|
47
|
+
type Result<T, E> = { ok: true; value: T } | { ok: false; error: E }
|
|
201
48
|
|
|
202
|
-
class
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
static getDerivedStateFromError(): ErrorBoundaryState {
|
|
206
|
-
return { hasError: true }
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
render() {
|
|
210
|
-
if (this.state.hasError) return this.props.fallback
|
|
211
|
-
return this.props.children
|
|
49
|
+
class AppError extends Error {
|
|
50
|
+
constructor(message: string, readonly code: string, readonly statusCode = 500) {
|
|
51
|
+
super(message); this.name = this.constructor.name
|
|
212
52
|
}
|
|
213
53
|
}
|
|
214
54
|
```
|
|
215
55
|
|
|
216
|
-
|
|
217
|
-
|
|
56
|
+
Error Boundary — class component が必要となる唯一の箇所:
|
|
218
57
|
```typescript
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function isApiError(error: unknown): error is ApiError {
|
|
226
|
-
return (
|
|
227
|
-
typeof error === 'object' &&
|
|
228
|
-
error !== null &&
|
|
229
|
-
'code' in error &&
|
|
230
|
-
'message' in error
|
|
231
|
-
)
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
async function fetchWithErrorHandling<T>(url: string): Promise<T> {
|
|
235
|
-
const response = await fetch(url)
|
|
236
|
-
|
|
237
|
-
if (!response.ok) {
|
|
238
|
-
const error: unknown = await response.json()
|
|
239
|
-
if (isApiError(error)) {
|
|
240
|
-
throw new ApiError(error.code, error.message)
|
|
241
|
-
}
|
|
242
|
-
throw new Error('Unknown API error')
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return response.json() as Promise<T>
|
|
246
|
-
}
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
## イベントハンドリング
|
|
250
|
-
|
|
251
|
-
### イベント型
|
|
252
|
-
|
|
253
|
-
```typescript
|
|
254
|
-
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
255
|
-
e.preventDefault()
|
|
256
|
-
// handle click
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
260
|
-
const value = e.target.value
|
|
261
|
-
// handle change
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
|
265
|
-
e.preventDefault()
|
|
266
|
-
// handle submit
|
|
58
|
+
class ErrorBoundary extends React.Component<{ children: React.ReactNode; fallback: React.ReactNode }, { hasError: boolean }> {
|
|
59
|
+
state = { hasError: false }
|
|
60
|
+
static getDerivedStateFromError() { return { hasError: true } }
|
|
61
|
+
render() { return this.state.hasError ? this.props.fallback : this.props.children }
|
|
267
62
|
}
|
|
268
63
|
```
|
|
269
64
|
|
|
270
|
-
##
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
-
|
|
274
|
-
-
|
|
275
|
-
-
|
|
276
|
-
- **定数**: SCREAMING_SNAKE_CASE(例: `MAX_RETRY_COUNT`)
|
|
277
|
-
- **ファイル名**: コンポーネントはPascalCase、その他はcamelCase
|
|
278
|
-
|
|
279
|
-
### インポート順序
|
|
280
|
-
1. React関連
|
|
281
|
-
2. 外部ライブラリ
|
|
282
|
-
3. 内部モジュール(絶対パス)
|
|
283
|
-
4. 内部モジュール(相対パス)
|
|
284
|
-
5. 型のみのインポート
|
|
285
|
-
6. スタイル/アセット
|
|
286
|
-
|
|
287
|
-
```typescript
|
|
288
|
-
import { useState, useEffect } from 'react'
|
|
289
|
-
import { useQuery } from '@tanstack/react-query'
|
|
290
|
-
import { api } from '@/lib/api'
|
|
291
|
-
import { formatDate } from '../utils'
|
|
292
|
-
import type { User } from '@/types'
|
|
293
|
-
import styles from './Component.module.css'
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
## アンチパターン
|
|
297
|
-
|
|
298
|
-
### 避けるべきパターン
|
|
299
|
-
|
|
300
|
-
```typescript
|
|
301
|
-
// 悪い: Propsのスプレッド展開
|
|
302
|
-
const Button = (props: ButtonProps) => <button {...props} />
|
|
303
|
-
|
|
304
|
-
// 良い: 明示的なPropsの受け渡し
|
|
305
|
-
const Button = ({ label, onClick, disabled }: ButtonProps) => (
|
|
306
|
-
<button onClick={onClick} disabled={disabled}>{label}</button>
|
|
307
|
-
)
|
|
308
|
-
|
|
309
|
-
// 悪い: インラインでの複雑なロジック
|
|
310
|
-
{items.filter(i => i.active).map(i => <Item key={i.id} {...i} />)}
|
|
311
|
-
|
|
312
|
-
// 良い: 事前に変数として抽出
|
|
313
|
-
const activeItems = items.filter(item => item.active)
|
|
314
|
-
{activeItems.map(item => <Item key={item.id} item={item} />)}
|
|
315
|
-
```
|
|
65
|
+
## プロジェクト規約
|
|
66
|
+
- **環境変数:** ビルドツールの環境変数システム経由で読む(ブラウザに `process.env` は存在しない)。秘密情報はすべてサーバーサイドに置く — フロントエンドのコードはクライアントに配信される。
|
|
67
|
+
- **バンドルとパフォーマンス:** `build` スクリプトで監視し 500KB 未満に保つ。高コストなコンポーネントは `React.memo` でメモ化する。`React.lazy` + `Suspense` でコード分割する。再レンダリングを最小化する状態構造にする。
|
|
68
|
+
- **命名:** コンポーネント/型は `PascalCase`、変数/関数は `camelCase`、hook は `use` 接頭辞、定数は `SCREAMING_SNAKE_CASE`。
|
|
69
|
+
- **インポート:** `src/` からの絶対パス。順序: React → 外部ライブラリ → 内部(絶対)→ 内部(相対)→ 型のみ → スタイル/アセット。
|
|
70
|
+
- **フォーマット:** Biome に従う(セミコロンやスタイルはプロジェクト設定に従う)。
|
|
@@ -5,6 +5,13 @@ description: React Testing Library、MSW、Playwright E2Eでテストを設計
|
|
|
5
5
|
|
|
6
6
|
# TypeScript テストルール(フロントエンド)
|
|
7
7
|
|
|
8
|
+
## 参照
|
|
9
|
+
|
|
10
|
+
| テスト種別 | 参照先 | 用途 |
|
|
11
|
+
|-----------|--------|------|
|
|
12
|
+
| **ユニット / 統合** | 本ドキュメント | RTL + Vitest + MSW での React コンポーネントテスト |
|
|
13
|
+
| **E2E** | [references/e2e.md](references/e2e.md) | Playwright によるブラウザレベル E2E テスト |
|
|
14
|
+
|
|
8
15
|
## テストフレームワーク
|
|
9
16
|
- **Vitest**: このプロジェクトではVitestを使用
|
|
10
17
|
- **React Testing Library**: コンポーネントテスト用
|
|
@@ -131,75 +138,12 @@ describe('Button', () => {
|
|
|
131
138
|
})
|
|
132
139
|
```
|
|
133
140
|
|
|
134
|
-
## テスト品質基準
|
|
135
|
-
|
|
136
|
-
### 境界値・異常系の網羅
|
|
137
|
-
正常系に加え、境界値と異常系を含める。
|
|
138
|
-
```typescript
|
|
139
|
-
it('renders empty state for empty array', () => {
|
|
140
|
-
render(<UserList users={[]} />)
|
|
141
|
-
expect(screen.getByText('ユーザーがいません')).toBeInTheDocument()
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
it('displays error message on API failure', async () => {
|
|
145
|
-
server.use(rest.get('/api/users', (req, res, ctx) => res(ctx.status(500))))
|
|
146
|
-
render(<UserList />)
|
|
147
|
-
expect(await screen.findByText('エラーが発生しました')).toBeInTheDocument()
|
|
148
|
-
})
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
### ユーザー中心のクエリ
|
|
152
|
-
|
|
153
|
-
```typescript
|
|
154
|
-
// 良い: アクセシブルなクエリ
|
|
155
|
-
screen.getByRole('button', { name: 'Submit' })
|
|
156
|
-
screen.getByLabelText('Email')
|
|
157
|
-
screen.getByText('Welcome')
|
|
158
|
-
|
|
159
|
-
// 悪い: 実装詳細への依存
|
|
160
|
-
screen.getByTestId('submit-btn')
|
|
161
|
-
container.querySelector('.btn-primary')
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
### 非同期処理のテスト
|
|
165
|
-
|
|
166
|
-
```typescript
|
|
167
|
-
it('loads and displays user data', async () => {
|
|
168
|
-
render(<UserProfile userId="1" />)
|
|
169
|
-
|
|
170
|
-
// ローディング状態を確認
|
|
171
|
-
expect(screen.getByText('Loading...')).toBeInTheDocument()
|
|
172
|
-
|
|
173
|
-
// データ表示を待機
|
|
174
|
-
expect(await screen.findByText('John Doe')).toBeInTheDocument()
|
|
175
|
-
|
|
176
|
-
// ローディングが消えていることを確認
|
|
177
|
-
expect(screen.queryByText('Loading...')).not.toBeInTheDocument()
|
|
178
|
-
})
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
### フォームテスト
|
|
182
|
-
|
|
183
|
-
```typescript
|
|
184
|
-
it('submits form with valid data', async () => {
|
|
185
|
-
const onSubmit = vi.fn()
|
|
186
|
-
render(<LoginForm onSubmit={onSubmit} />)
|
|
187
|
-
|
|
188
|
-
await userEvent.type(screen.getByLabelText('Email'), 'test@example.com')
|
|
189
|
-
await userEvent.type(screen.getByLabelText('Password'), 'password123')
|
|
190
|
-
await userEvent.click(screen.getByRole('button', { name: 'Login' }))
|
|
191
|
-
|
|
192
|
-
expect(onSubmit).toHaveBeenCalledWith({
|
|
193
|
-
email: 'test@example.com',
|
|
194
|
-
password: 'password123'
|
|
195
|
-
})
|
|
196
|
-
})
|
|
197
|
-
```
|
|
198
|
-
|
|
199
141
|
## テスト設計パターン
|
|
200
142
|
|
|
143
|
+
実装詳細ではなくユーザーから見える結果を検証する。クエリはアクセシビリティ優先(`getByRole`/`getByLabelText`/`getByText`)で、`getByTestId` や `container.querySelector` に依存しない。正常系だけでなく空・エラー・ローディング/非同期の状態も網羅し、非同期UIは `findBy*` で待機する。
|
|
144
|
+
|
|
201
145
|
```typescript
|
|
202
|
-
//
|
|
146
|
+
// ユーザーから見える結果を検証
|
|
203
147
|
it('increments count when clicked', async () => {
|
|
204
148
|
const user = userEvent.setup()
|
|
205
149
|
render(<Counter />)
|
|
@@ -207,10 +151,10 @@ it('increments count when clicked', async () => {
|
|
|
207
151
|
expect(screen.getByText('Count: 1')).toBeInTheDocument()
|
|
208
152
|
})
|
|
209
153
|
|
|
210
|
-
//
|
|
211
|
-
it('
|
|
212
|
-
|
|
213
|
-
render(<
|
|
214
|
-
|
|
154
|
+
// エラー状態: 1テストだけハンドラを上書き
|
|
155
|
+
it('shows an error message on API failure', async () => {
|
|
156
|
+
server.use(http.get('/api/users', () => new HttpResponse(null, { status: 500 })))
|
|
157
|
+
render(<UserList />)
|
|
158
|
+
expect(await screen.findByText('エラーが発生しました')).toBeInTheDocument()
|
|
215
159
|
})
|
|
216
160
|
```
|
|
@@ -156,7 +156,7 @@ description: サブエージェントのタスク分担と連携を調整。規
|
|
|
156
156
|
| code-verifier | status (consistent/mostly_consistent/needs_review/inconsistent), consistencyScore, discrepancies[], reverseCoverage (dataOperationsInCode, testBoundariesSectionPresent). 実装前: Design Docの主張を既存コードに対して検証。実装後: 実装のDesign Doc整合性を検証(`code_paths`で変更ファイルにスコープ) | discrepanciesをdocument-reviewerに連携 |
|
|
157
157
|
| task-executor | 入力: `task_file`(オーケストレーションフローでは必須); 任意の Fix Mode シグナル `requiredFixes` または `incompleteImplementations` — いずれかが非空の場合、`task_already_completed` チェックをスキップし、各項目の `file_path` / `location`(`location` は `file[:line]` として解釈)で許可リストを拡張する。`incompleteImplementations[]` の各エントリは `type: "missing_logic" \| "hollow_test"` を持ち得て、executor は `type` で修正アクションを分岐する。出力: status (escalation_needed/completed), filesModified[], testsAdded, requiresTestReview, runnableCheck{level, executed, command, result, substance, substanceIssue, reason}, escalation_type ∈ {task_file_not_found, task_already_completed, target_files_missing, design_compliance_violation, similar_function_found, similar_component_found, investigation_target_not_found, out_of_scope_file, dependency_version_uncertain, binding_decision_violation, test_environment_not_ready} | escalation_needed時: escalation_type別に対応 |
|
|
158
158
|
| quality-fixer | 入力: `task_file`(現在のタスクファイルパス — オーケストレーションフローでは常に渡す)、`filesModified`(上流の実装ステップのレスポンスから抽出 — 当該タスクの書き込み集合を未完成実装検出の主要スコープとして渡す。省略時は `git diff HEAD` にフォールバック)、`runnableCheck`(上流の実装ステップのレスポンスから抽出 — `substance` と `substanceIssue` を含むテスト実行のエビデンスを渡し、Substance チェックが実行時のシグナルを受け取れるようにする。上流がテストを実行していない場合は省略可)。Status: approved/stub_detected/blocked。`stub_detected` → `incompleteImplementations[]` の各エントリは `type: "missing_logic" \| "hollow_test"` を持ち、`type` で executor 側の修正アクションを分岐させた上で上流の実装ステップに差し戻し、本実装完了後にquality-fixerを再実行。`blocked` → 下記quality-fixer blockedハンドリング参照 | stub_detected: 実装ステップを再実行。blocked: 下記参照 |
|
|
159
|
-
| document-reviewer |
|
|
159
|
+
| document-reviewer | verdict.decision (approved/approved_with_conditions/needs_revision/rejected) | approved/approved_with_conditionsで次へ。needs_revisionで修正依頼。rejectedでエスカレーション |
|
|
160
160
|
| design-sync | sync_status (NO_CONFLICTS/CONFLICTS_FOUND) | CONFLICTS_FOUND時: 矛盾をユーザーに提示してから進む |
|
|
161
161
|
| integration-test-reviewer | status (approved/needs_revision/blocked), requiredFixes | needs_revision時: 同じ task_file と requiredFixes[] を渡してルーティング先の executor を Fix Mode で再実行 |
|
|
162
162
|
| security-reviewer | status (approved/approved_with_notes/needs_revision/blocked), findings, notes, requiredFixes | needs_revision時: `requiredFixes[].location` から影響ファイルパスを抽出して Target Files に投入した統合修正タスクファイルを作成し、その task_file と `requiredFixes[]` 配列を渡してルーティング先の executor を Fix Mode で起動。続いて quality-fixer を実行し、最後に security-reviewer を再起動して解消を検証する。blocked 時: ブロッキング findings を添えてユーザーにエスカレーション — エージェント層の権限外の修正である |
|
|
@@ -170,7 +170,7 @@ quality-fixerが `status: "blocked"` を返した場合、`reason`で判別:
|
|
|
170
170
|
|
|
171
171
|
## 作業計画時の基本フロー
|
|
172
172
|
|
|
173
|
-
### 大規模(6ファイル以上) -
|
|
173
|
+
### 大規模(6ファイル以上) - 14ステップ(バックエンド) / 16ステップ(フロントエンド/フルスタック)
|
|
174
174
|
|
|
175
175
|
1. requirement-analyzer → 要件分析 + 既存PRD確認 **[停止]**
|
|
176
176
|
2. prd-creator → PRD作成
|
|
@@ -185,10 +185,11 @@ quality-fixerが `status: "blocked"` を返した場合、`reason`で判別:
|
|
|
185
185
|
11. document-reviewer → Design Docレビュー(code-verifier結果をcode_verificationとして入力。レイヤー横断時: Design Doc毎に実行)
|
|
186
186
|
12. design-sync → 整合性検証 **[停止: Design Doc承認]**
|
|
187
187
|
13. acceptance-test-generator → テストスケルトン生成、work-plannerに渡す (*1)
|
|
188
|
-
14. work-planner → 作業計画書作成
|
|
189
|
-
15.
|
|
188
|
+
14. work-planner → 作業計画書作成
|
|
189
|
+
15. document-reviewer → 作業計画書レビュー(doc_type: WorkPlan。AC/コントラクト/状態のカバレッジをトレースできるようDesign Docのパスを渡す)。`needs_revision` の場合: work-plannerを(updateで)再実行し `approved`/`approved_with_conditions` になるまで再レビューする — 作業計画書はDesign Docの派生物であるため、計画の忠実性に関する指摘にユーザー裁定は不要。`rejected` の場合: ユーザーにエスカレーション。 **[停止: 一括承認]**
|
|
190
|
+
16. task-decomposer → 自律実行 → 完了報告
|
|
190
191
|
|
|
191
|
-
### 中規模(3-5ファイル) -
|
|
192
|
+
### 中規模(3-5ファイル) - 10ステップ(バックエンド) / 12ステップ(フロントエンド/フルスタック)
|
|
192
193
|
|
|
193
194
|
1. requirement-analyzer → 要件分析 **[停止]**
|
|
194
195
|
2. **(フロントエンド/フルスタックのみ)** プロトタイプコードの有無を確認 → ui-spec-designer → UI Spec作成(コンポーネント構造が技術設計に反映されるため先に実施)
|
|
@@ -199,8 +200,9 @@ quality-fixerが `status: "blocked"` を返した場合、`reason`で判別:
|
|
|
199
200
|
7. document-reviewer → Design Docレビュー(code-verifier結果をcode_verificationとして入力。レイヤー横断時: Design Doc毎に実行)
|
|
200
201
|
8. design-sync → 整合性検証 **[停止: Design Doc承認]**
|
|
201
202
|
9. acceptance-test-generator → テストスケルトン生成、work-plannerに渡す (*1)
|
|
202
|
-
10. work-planner → 作業計画書作成
|
|
203
|
-
11.
|
|
203
|
+
10. work-planner → 作業計画書作成
|
|
204
|
+
11. document-reviewer → 作業計画書レビュー(doc_type: WorkPlan。AC/コントラクト/状態のカバレッジをトレースできるようDesign Docのパスを渡す)。`needs_revision` の場合: work-plannerを(updateで)再実行し `approved`/`approved_with_conditions` になるまで再レビューする — 作業計画書はDesign Docの派生物であるため、計画の忠実性に関する指摘にユーザー裁定は不要。`rejected` の場合: ユーザーにエスカレーション。 **[停止: 一括承認]**
|
|
205
|
+
12. task-decomposer → 自律実行 → 完了報告
|
|
204
206
|
|
|
205
207
|
### 小規模(1-2ファイル) - 2ステップ
|
|
206
208
|
|
|
@@ -183,20 +183,19 @@ skills:
|
|
|
183
183
|
# フロントエンド固有スキル
|
|
184
184
|
frontend-typescript-rules:
|
|
185
185
|
skill: "frontend-typescript-rules"
|
|
186
|
-
tags: [frontend, react, implementation, type-safety, props-driven, hooks,
|
|
187
|
-
typical-use: "React
|
|
186
|
+
tags: [frontend, react, implementation, type-safety, component-design, props-driven, hooks, error-handling, conventions]
|
|
187
|
+
typical-use: "Reactコンポーネント実装、Props/状態設計、エラーハンドリング、frontend TypeScript実装ルール"
|
|
188
188
|
size: small
|
|
189
189
|
key-references:
|
|
190
|
-
- "React
|
|
191
|
-
- "
|
|
190
|
+
- "React Function Components - React docs"
|
|
191
|
+
- "Type guards at external boundaries (unknown narrowing)"
|
|
192
|
+
- "Result type and Error Boundary error handling"
|
|
192
193
|
sections:
|
|
193
|
-
- "
|
|
194
|
-
- "
|
|
195
|
-
- "
|
|
194
|
+
- "アンチパターンとしきい値"
|
|
195
|
+
- "境界での型安全性"
|
|
196
|
+
- "コンポーネントと状態の設計"
|
|
196
197
|
- "エラーハンドリング"
|
|
197
|
-
- "
|
|
198
|
-
- "コーディング規約"
|
|
199
|
-
- "アンチパターン"
|
|
198
|
+
- "プロジェクト規約"
|
|
200
199
|
|
|
201
200
|
frontend-typescript-testing:
|
|
202
201
|
skill: "frontend-typescript-testing"
|
|
@@ -209,12 +208,12 @@ skills:
|
|
|
209
208
|
- "ADR-0002 Co-location原則"
|
|
210
209
|
- "references/e2e.md - Playwright E2Eパターン"
|
|
211
210
|
sections:
|
|
211
|
+
- "参照"
|
|
212
212
|
- "テストフレームワーク"
|
|
213
213
|
- "テストの基本方針"
|
|
214
214
|
- "テストの実装規約"
|
|
215
215
|
- "モックの型安全性の徹底"
|
|
216
216
|
- "React Testing Libraryの基本例"
|
|
217
|
-
- "テスト品質基準"
|
|
218
217
|
- "テスト設計パターン"
|
|
219
218
|
|
|
220
219
|
frontend-technical-spec:
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.23.3] - 2026-06-06
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Requirement-boundary fidelity gates** (agents) — Behavior changes are specified, proven, and verified at the requirement boundaries where regressions hide (a behavior can hold on the main path while regressing on a separate branch / state / input / lifecycle / fallback dimension). `technical-designer` / `-frontend` draft each AC value-first and expand it across requirement boundaries before scoping; `acceptance-test-generator` records a boundary-path proof obligation so the generated test traverses the path that would otherwise stay green through a regression; `task-executor` / `-frontend` add a Step 4 Core Mechanism Preservation Check that escalates `design_compliance_violation` when a required mechanism would be replaced by a weaker substitute or is infeasible; `code-reviewer` verifies boundary-path evidence, core-mechanism preservation, and publication-boundary state at AC verification
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- **technical-designer prompt trimming** (agents) — Compressed the Gate Ordering section to a single serial-gate line, with gate membership and applicability conditions retained in each subsection's `[Gate N — ...]` heading; removed the inline React implementation sample from `technical-designer-frontend` in favor of the loaded `frontend-typescript-rules` skill
|
|
17
|
+
- **Frontend skill alignment across en/ja** (skills) — `frontend-typescript-rules` is a rules-first, self-contained rewrite (thresholds, boundary type safety, component/state rules, and `Result` / `AppError` / Error Boundary idioms; generic syntax examples and `React.FC` dropped); `frontend-typescript-testing` unifies the en/ja structure, routes E2E to `references/e2e.md`, and uses MSW v2 (`http` / `HttpResponse`); `frontend-technical-spec` uses the `VITE_` prefix for client-exposed environment variables
|
|
18
|
+
|
|
19
|
+
## [1.23.2] - 2026-06-01
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- **Proof-quality review gate** (agents, skills) — Verification is raised from "tests exist" to "tests prove the claim's primary failure mode." `acceptance-test-generator` emits `Primary failure mode` / `Proof obligation` annotations on every skeleton; `work-planner`, `task-decomposer`, plan-template, and task-template carry per-claim Proof Obligations into task files; `integration-test-reviewer` adds a Claim Proof Adequacy check with a `proof_insufficient` issue type; `code-reviewer` requires AC-coverage tests to turn red under the recorded primary failure mode and exercise the claimed boundary directly
|
|
24
|
+
- **Planning-fidelity review gate** (agents, commands, skills) — The work plan now gets the same automated review the Design Doc does, before implementation starts. `document-reviewer` gains a `WorkPlan` doc_type semantic gate (Design-to-Plan traceability, early-verification placement, real-boundary coverage, Failure Mode Checklist, Review Scope) with a verdict mapping and a `design_doc` input; `work-planner` and plan-template add a Proof Strategy, Review Scope, and Failure Mode Checklist; the plan / front-plan recipes review the plan and self-heal `needs_revision` (re-plan and re-review) while escalating `rejected`; front-build reviews a plan it creates from a Design Doc before decomposition; `subagents-orchestration-guide` wires WorkPlan review into the Medium/Large planning flow and moves the `document-reviewer` output contract to `verdict.decision`
|
|
25
|
+
|
|
8
26
|
## [1.23.1] - 2026-05-28
|
|
9
27
|
|
|
10
28
|
### Added
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-ai-project",
|
|
3
|
-
"version": "1.23.
|
|
3
|
+
"version": "1.23.3",
|
|
4
4
|
"packageManager": "npm@10.8.2",
|
|
5
5
|
"description": "TypeScript boilerplate with skills and sub-agents for Claude Code. Prevents context exhaustion through role-based task splitting.",
|
|
6
6
|
"keywords": [
|