create-ai-project 1.23.2 → 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 +2 -1
- package/.claude/agents-en/code-reviewer.md +3 -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-ja/acceptance-test-generator.md +2 -1
- package/.claude/agents-ja/code-reviewer.md +3 -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/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/task-analyzer/references/skills-index.yaml +9 -11
- 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/task-analyzer/references/skills-index.yaml +10 -11
- package/CHANGELOG.md +11 -0
- package/package.json +1 -1
|
@@ -68,6 +68,7 @@ For each valid AC from Phase 1:
|
|
|
68
68
|
- Happy path (1 test mandatory)
|
|
69
69
|
- Error handling (only user-visible errors)
|
|
70
70
|
- Edge cases (only if high business impact)
|
|
71
|
+
- Boundary path (behavior-changing AC only): when the AC can hold on the main path while a distinct branch, state, input class, lifecycle step, or fallback regresses, capture that boundary as a proof obligation so the test exercises it
|
|
71
72
|
|
|
72
73
|
2. **Classify test level**:
|
|
73
74
|
- Integration test candidate (feature-level interaction)
|
|
@@ -190,7 +191,7 @@ describe('[Feature Name] Integration Test', () => {
|
|
|
190
191
|
|
|
191
192
|
**Proof annotations** (apply to every skeleton, alongside the metadata above): each `it.todo` carries two comment lines that hand the proof contract to the test implementer and to integration-test-reviewer (these map to the task template's Proof Obligations fields):
|
|
192
193
|
- `Primary failure mode`: the specific regression that turns this test red — the behavior the AC promises and would break
|
|
193
|
-
- `Proof obligation`: what the implemented test must assert to prove the claim — the boundary to traverse, the observable state before/after for state-changing ACs, and which boundaries may be mocked and why. Phrase it as design intent describing what to assert; the implementer writes the executable assertions and mock setup
|
|
194
|
+
- `Proof obligation`: what the implemented test must assert to prove the claim — the boundary to traverse, the observable state before/after for state-changing ACs, and which boundaries may be mocked and why. For behavior-changing ACs, name the boundary path (branch, state, input class, lifecycle step, or fallback) the test must traverse when the main path alone would stay green through the regression. Phrase it as design intent describing what to assert; the implementer writes the executable assertions and mock setup
|
|
194
195
|
|
|
195
196
|
### E2E Test Files
|
|
196
197
|
|
|
@@ -62,6 +62,9 @@ For each acceptance criterion extracted in Step 1:
|
|
|
62
62
|
- Determine status: fulfilled / partially fulfilled / unfulfilled
|
|
63
63
|
- Record the file path and relevant code location
|
|
64
64
|
- Note any deviations from the Design Doc specification
|
|
65
|
+
- For behavior-changing ACs, confirm the evidence covers the boundary paths, not only the main path: where a distinct branch, state, input class, lifecycle step, or fallback governs the behavior, verify it is exercised. Compare the source/referenced behavior and the implemented behavior at the same granularity; an unsupported change in a boundary dimension is a `dd_violation`
|
|
66
|
+
- Confirm the implementation keeps the core mechanism the AC, Design Doc, or referenced materials explicitly require; cite the source phrase. A simpler substitute that passes tests but drops the required mechanism is a `dd_violation`
|
|
67
|
+
- For changes to persisted, shared, or externally observable state, identify the publication boundary (where the new state becomes observable to another process, component, user, or later step). State that is observable as complete while still partial, uninitialized, stale, or rollback-only is a `reliability` finding, because a downstream consumer can treat the incomplete state as complete and fail
|
|
65
68
|
|
|
66
69
|
#### 2-2. Identifier Verification
|
|
67
70
|
|
|
@@ -94,6 +94,12 @@ Escalation thresholds:
|
|
|
94
94
|
- Exactly the pair (a+c) or (b+c) → Escalation; any other 2-indicator combination → Continue
|
|
95
95
|
- 1 or fewer indicators match → Continue implementation
|
|
96
96
|
|
|
97
|
+
### Step4: Core Mechanism Preservation Check (Any YES → Immediate Escalation)
|
|
98
|
+
Preserve the core mechanism the task, AC, Design Doc, or UI Spec requires. Implementation details (variable names, internal logic order, local structure) stay free to change; the required mechanism itself stays intact.
|
|
99
|
+
□ Required core mechanism replaced by a simpler or weaker substitute? (passing tests do not make a substitute acceptable)
|
|
100
|
+
□ Required core mechanism infeasible as specified?
|
|
101
|
+
Any YES → stop and escalate with `escalation_type: "design_compliance_violation"` per the Escalation Response table, mapping the case to the contract fields: `design_doc_expectation` = the required mechanism and the source phrase it cites (task/AC/Design Doc/UI Spec); `actual_situation` = the proposed substitute and the resulting behavioral delta; `why_cannot_implement` = why the required mechanism was replaced or is infeasible as specified; `attempted_approaches[]` = the ways attempted to preserve the required mechanism, or `[]` when infeasibility is known before implementation; `claude_recommendation` = the condition that would lift the block.
|
|
102
|
+
|
|
97
103
|
### Boundary Cases and Iron Rule
|
|
98
104
|
|
|
99
105
|
| Case | Continue | Escalate |
|
|
@@ -105,7 +111,7 @@ Escalation thresholds:
|
|
|
105
111
|
|
|
106
112
|
**Iron Rule — escalate when objectively undeterminable**: 2+ valid interpretations for a judgment item; pattern unprecedented in past implementation experience; required information not in Design Doc; equivalent engineers would split on the call.
|
|
107
113
|
|
|
108
|
-
### Implementation Continuable (all Step1-
|
|
114
|
+
### Implementation Continuable (all Step1-4 checks NO and clearly applicable)
|
|
109
115
|
Internal detail optimization (variable names, logic order); specs not in Design Doc; safe `unknown` → concrete type guard for external API responses; minor UI/message text adjustments.
|
|
110
116
|
|
|
111
117
|
## Responsibilities, Authority, and Boundaries
|
|
@@ -94,6 +94,12 @@ Escalation thresholds:
|
|
|
94
94
|
- Exactly the pair (a+c) or (b+c) → Escalation; any other 2-indicator combination → Continue
|
|
95
95
|
- 1 or fewer indicators match → Continue implementation
|
|
96
96
|
|
|
97
|
+
### Step4: Core Mechanism Preservation Check (Any YES → Immediate Escalation)
|
|
98
|
+
Preserve the core mechanism the task, AC, Design Doc, or referenced materials require. Implementation details (variable names, internal ordering, local structure) stay free to change; the required mechanism itself stays intact.
|
|
99
|
+
□ Required core mechanism replaced by a simpler or weaker substitute? (passing tests do not make a substitute acceptable)
|
|
100
|
+
□ Required core mechanism infeasible as specified?
|
|
101
|
+
Any YES → stop and escalate with `escalation_type: "design_compliance_violation"` per the Escalation Response table, mapping the case to the contract fields: `design_doc_expectation` = the required mechanism and the source phrase it cites (task/AC/Design Doc/referenced material); `actual_situation` = the proposed substitute and the resulting behavioral delta; `why_cannot_implement` = why the required mechanism was replaced or is infeasible as specified; `attempted_approaches[]` = the ways attempted to preserve the required mechanism, or `[]` when infeasibility is known before implementation; `claude_recommendation` = the condition that would lift the block.
|
|
102
|
+
|
|
97
103
|
### Boundary Cases and Iron Rule
|
|
98
104
|
|
|
99
105
|
| Case | Continue | Escalate |
|
|
@@ -105,7 +111,7 @@ Escalation thresholds:
|
|
|
105
111
|
|
|
106
112
|
**Iron Rule — escalate when objectively undeterminable**: 2+ valid interpretations for a judgment item; pattern unprecedented in past implementation experience; required information not in Design Doc; equivalent engineers would split on the call.
|
|
107
113
|
|
|
108
|
-
### Implementation Continuable (all Step1-
|
|
114
|
+
### Implementation Continuable (all Step1-4 checks NO and clearly applicable)
|
|
109
115
|
Internal detail optimization (variable names, processing order); specs not in Design Doc; safe `unknown` → concrete type guard; minor UI/message text adjustments.
|
|
110
116
|
|
|
111
117
|
## Responsibilities, Authority, and Boundaries
|
|
@@ -30,29 +30,7 @@ Follow documentation-criteria skill for ADR/Design Doc creation thresholds. If a
|
|
|
30
30
|
|
|
31
31
|
### Gate Ordering [BLOCKING]
|
|
32
32
|
|
|
33
|
-
The subsections below are not parallel mandates; they form four serial gates. Complete each gate fully before starting the next.
|
|
34
|
-
|
|
35
|
-
**Gate 0 — Inputs and Standards** (no upstream dependencies):
|
|
36
|
-
- Agreement Checklist
|
|
37
|
-
- Standards Identification
|
|
38
|
-
|
|
39
|
-
**Gate 1 — Existing State Analysis** (depends on Gate 0):
|
|
40
|
-
- Existing Code Investigation
|
|
41
|
-
- Fact Disposition (when Codebase Analysis input is provided)
|
|
42
|
-
- Minimal Surface Alternatives (when introducing persistent client/server state, props or fields crossing ownership boundaries — public API props of exported reusable components, Context values, or state lifted across ownership boundaries — behavioral modes/variants that change observable behavior, or reusable component splits)
|
|
43
|
-
|
|
44
|
-
**Gate 2 — Design Decisions** (depends on Gate 1):
|
|
45
|
-
- Implementation Approach Decision
|
|
46
|
-
- Common ADR Process
|
|
47
|
-
- Data Contracts
|
|
48
|
-
- State Transitions (when applicable)
|
|
49
|
-
|
|
50
|
-
**Gate 3 — Impact Documentation** (depends on Gate 2):
|
|
51
|
-
- Integration Point Analysis
|
|
52
|
-
- Change Impact Map
|
|
53
|
-
- Interface Change Impact Analysis
|
|
54
|
-
|
|
55
|
-
Each subsection below carries a `[Gate N — ...]` annotation in its heading. Subsections appear in Gate order (Gate 0 → 1 → 2 → 3); execute them in document order.
|
|
33
|
+
The subsections below are not parallel mandates; they form four serial gates: **Gate 0** Inputs & Standards → **Gate 1** Existing-State Analysis → **Gate 2** Design Decisions → **Gate 3** Impact Documentation. Complete each gate fully before starting the next. Each subsection below carries a `[Gate N — ...]` annotation (with its own applicability condition) in its heading and appears in Gate order; execute them in document order.
|
|
56
34
|
|
|
57
35
|
### Agreement Checklist [Gate 0 — Required]
|
|
58
36
|
Must be performed at the beginning of Design Doc creation:
|
|
@@ -331,31 +309,7 @@ Consistency first (follow existing React component patterns; document reason whe
|
|
|
331
309
|
|
|
332
310
|
**MANDATORY**: implementation samples in ADR/Design Docs MUST comply with frontend-typescript-rules skill. Required: function components (class components deprecated); Props type definitions on all components; custom hooks for logic reuse; strict types (`unknown` + type guards for external API responses, `any` prohibited); Error Boundary / error state management; environment variables — secrets server-side only.
|
|
333
311
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
```typescript
|
|
337
|
-
type ButtonProps = { label: string; onClick: () => void; disabled?: boolean }
|
|
338
|
-
export function Button({ label, onClick, disabled = false }: ButtonProps) {
|
|
339
|
-
return <button onClick={onClick} disabled={disabled}>{label}</button>
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
function useUserData(userId: string) {
|
|
343
|
-
const [user, setUser] = useState<User | null>(null)
|
|
344
|
-
const [error, setError] = useState<Error | null>(null)
|
|
345
|
-
useEffect(() => {
|
|
346
|
-
void (async () => {
|
|
347
|
-
try {
|
|
348
|
-
const data: unknown = await (await fetch(`/api/users/${userId}`)).json()
|
|
349
|
-
if (!isUser(data)) throw new Error('Invalid user data')
|
|
350
|
-
setUser(data)
|
|
351
|
-
} catch (e) { setError(e instanceof Error ? e : new Error('Unknown error')) }
|
|
352
|
-
})()
|
|
353
|
-
}, [userId])
|
|
354
|
-
return { user, error }
|
|
355
|
-
}
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
Non-compliant: class components, `any`, untyped responses without guards, secrets embedded client-side.
|
|
312
|
+
Non-compliant: class components (except Error Boundaries), `any`, untyped responses without guards, secrets embedded client-side.
|
|
359
313
|
|
|
360
314
|
## Diagram Creation (mermaid)
|
|
361
315
|
|
|
@@ -394,6 +348,14 @@ Non-compliant: class components, `any`, untyped responses without guards, secret
|
|
|
394
348
|
|
|
395
349
|
## Acceptance Criteria Creation Guidelines
|
|
396
350
|
|
|
351
|
+
### Value-First Drafting and Boundary Expansion
|
|
352
|
+
|
|
353
|
+
Draft each AC value-first, then expand it across requirement boundaries before applying the scoping rules below:
|
|
354
|
+
|
|
355
|
+
1. **Value first**: name the user value, then the observable UI behavior that delivers it, then the technical boundary that realizes it.
|
|
356
|
+
2. **Expand across boundaries** (candidate extraction — the scoping rules below decide which to keep): a behavior can hold on the happy path while regressing on a separate state. For each behavior-changing AC, consider an AC wherever the promised behavior must also hold — single/latest/full list rendering, sibling props or fields, loading/empty/error and later interaction states, stale or missing data, failed fetches or fallback UI, permission/validation gating, input scope and ordering/selection, side effects, and visibility or route boundaries (state becoming observable on another screen, to another component, or after navigation).
|
|
357
|
+
3. **Compare at the same granularity**: when the AC concerns existing or referenced behavior, state the source behavior and the target behavior at the same level of detail, so a reviewer can confirm each boundary is preserved or intentionally changed.
|
|
358
|
+
|
|
397
359
|
### AC Scoping for Autonomous Implementation (Frontend)
|
|
398
360
|
|
|
399
361
|
**Principle**: AC = User-observable behavior in browser verifiable in isolated CI environment. Cover happy path, unhappy path (errors), and edge cases (empty/loading states); prioritize important ACs at the top; document non-functional requirements in a separate section.
|
|
@@ -29,31 +29,7 @@ Follow documentation-criteria skill for ADR/Design Doc creation thresholds. If a
|
|
|
29
29
|
|
|
30
30
|
### Gate Ordering [BLOCKING]
|
|
31
31
|
|
|
32
|
-
The subsections below are not parallel mandates; they form four serial gates. Complete each gate fully before starting the next.
|
|
33
|
-
|
|
34
|
-
**Gate 0 — Inputs and Standards** (no upstream dependencies):
|
|
35
|
-
- Agreement Checklist
|
|
36
|
-
- Standards Identification
|
|
37
|
-
|
|
38
|
-
**Gate 1 — Existing State Analysis** (depends on Gate 0):
|
|
39
|
-
- Existing Code Investigation
|
|
40
|
-
- Fact Disposition (when Codebase Analysis input is provided)
|
|
41
|
-
- Data Representation Decision (when new or modified data structures are introduced)
|
|
42
|
-
- Minimal Surface Alternatives (when introducing persistent state, public-contract elements or cross-boundary fields, behavioral modes/flags, or reusable abstractions)
|
|
43
|
-
|
|
44
|
-
**Gate 2 — Design Decisions** (depends on Gate 1):
|
|
45
|
-
- Implementation Approach Decision
|
|
46
|
-
- Common ADR Process
|
|
47
|
-
- Data Contracts
|
|
48
|
-
- State Transitions (when applicable)
|
|
49
|
-
|
|
50
|
-
**Gate 3 — Impact Documentation** (depends on Gate 2):
|
|
51
|
-
- Integration Points
|
|
52
|
-
- Change Impact Map
|
|
53
|
-
- Field Propagation Map (when fields cross component boundaries)
|
|
54
|
-
- Interface Change Impact Analysis
|
|
55
|
-
|
|
56
|
-
Each subsection below carries a `[Gate N — ...]` annotation in its heading. Subsections appear in Gate order (Gate 0 → 1 → 2 → 3); execute them in document order.
|
|
32
|
+
The subsections below are not parallel mandates; they form four serial gates: **Gate 0** Inputs & Standards → **Gate 1** Existing-State Analysis → **Gate 2** Design Decisions → **Gate 3** Impact Documentation. Complete each gate fully before starting the next. Each subsection below carries a `[Gate N — ...]` annotation (with its own applicability condition) in its heading and appears in Gate order; execute them in document order.
|
|
57
33
|
|
|
58
34
|
### Agreement Checklist [Gate 0 — Required]
|
|
59
35
|
Must be performed at the beginning of Design Doc creation:
|
|
@@ -370,6 +346,14 @@ Consistency first (follow existing patterns; document reason when introducing ne
|
|
|
370
346
|
|
|
371
347
|
## Acceptance Criteria Creation Guidelines
|
|
372
348
|
|
|
349
|
+
### Value-First Drafting and Boundary Expansion
|
|
350
|
+
|
|
351
|
+
Draft each AC value-first, then expand it across requirement boundaries before applying the rules below:
|
|
352
|
+
|
|
353
|
+
1. **Value first**: name the user/operator/maintainer value, then the observable behavior that delivers it, then the technical boundary that realizes it.
|
|
354
|
+
2. **Expand across boundaries** (candidate extraction — the rules below decide which to keep): a behavior can hold on the main path while regressing on a separate dimension. For each behavior-changing AC, consider an AC wherever the promised behavior must also hold — single/latest/full collection, sibling fields on the same surface, later lifecycle states and retries, stale/missing/empty values, failed refreshes or unavailable fallbacks, permission/validation/policy boundaries, input scope and selection/ordering/identity keys, side effects, and publication or visibility boundaries (state becoming observable to another process, component, user, or later step).
|
|
355
|
+
3. **Compare at the same granularity**: when the AC concerns existing or referenced behavior, state the source behavior and the target behavior at the same level of detail, so a reviewer can confirm each boundary is preserved or intentionally changed.
|
|
356
|
+
|
|
373
357
|
### Writing Measurable ACs
|
|
374
358
|
|
|
375
359
|
**Core Principle**: AC = User-observable behavior verifiable in isolated environment. Cover happy path, unhappy path, and edge cases. Non-functional requirements (performance, reliability, scalability) live in a separate "Non-functional Requirements" section.
|
|
@@ -453,4 +437,4 @@ Mode for documenting existing architecture as-is. Used when creating Design Docs
|
|
|
453
437
|
### Reverse-Engineer Mode Quality Standard
|
|
454
438
|
- Every claim cites file:line as evidence
|
|
455
439
|
- Identifiers transcribed exactly from code
|
|
456
|
-
- Test existence confirmed by Glob, not assumed
|
|
440
|
+
- Test existence confirmed by Glob, not assumed
|
|
@@ -68,6 +68,7 @@ Phase 1から有効な各ACについて:
|
|
|
68
68
|
- ハッピーパス(1テスト必須)
|
|
69
69
|
- エラーハンドリング(ユーザーから見えるエラーのみ)
|
|
70
70
|
- エッジケース(ビジネス影響が高い場合のみ)
|
|
71
|
+
- 境界パス(振る舞いを変えるACのみ): ACがメインパスでは成立しつつ、別個の分岐・状態・入力クラス・ライフサイクルステップ・フォールバックで退行しうる場合、その境界を証明義務として捉え、テストがそこを通過するようにする
|
|
71
72
|
|
|
72
73
|
2. **テストレベルを分類**:
|
|
73
74
|
- 統合テスト候補(機能レベルの相互作用)
|
|
@@ -192,7 +193,7 @@ describe('[機能名] Integration Test', () => {
|
|
|
192
193
|
|
|
193
194
|
**証明注釈**(すべてのスケルトンに、上記メタ情報とともに付与): 各 `it.todo` は証明コントラクトをテスト実装者と integration-test-reviewer に渡す2行のコメントを持つ(これらは task template の Proof Obligations フィールドに対応する):
|
|
194
195
|
- `主要な故障モード`: このテストをレッドにする具体的なリグレッション — ACが約束し、壊れると失われる振る舞い
|
|
195
|
-
- `証明義務`: 実装されたテストが主張を証明するためにアサートすべき内容 — 通過する境界、状態変更を伴うAC
|
|
196
|
+
- `証明義務`: 実装されたテストが主張を証明するためにアサートすべき内容 — 通過する境界、状態変更を伴うACでは操作前後の観測可能な状態、どの境界をなぜモックしてよいか。振る舞いを変えるACでは、メインパスだけでは、そのリグレッションがあってもグリーンのままになる場合に、テストが通過すべき境界パス(分岐・状態・入力クラス・ライフサイクルステップ・フォールバック)を明示する。アサート対象を記述する設計意図として書き、実行可能なアサーションとモック設定は実装者が書く
|
|
196
197
|
|
|
197
198
|
### E2Eテストファイル群
|
|
198
199
|
|
|
@@ -62,6 +62,9 @@ Step 1で抽出した各受入条件について:
|
|
|
62
62
|
- ステータスを判定: fulfilled / partially fulfilled / unfulfilled
|
|
63
63
|
- ファイルパスと関連コード箇所を記録
|
|
64
64
|
- Design Doc仕様からの逸脱を記録
|
|
65
|
+
- 振る舞いを変えるACでは、エビデンスがメインパスだけでなく境界パスもカバーしていることを確認する。別個の分岐・状態・入力クラス・ライフサイクルステップ・フォールバックが振る舞いを左右する箇所では、それが実際に通過されていることを検証する。参照元(source)/参照先の振る舞いと実装された振る舞いを同一粒度で比較し、境界次元における根拠のない変更は `dd_violation` とする
|
|
66
|
+
- 実装が AC・Design Doc・参照資料が明示的に要求する中核メカニズムを保持していることを確認し、出所となる文言を引用する。テストは通るが要求された中核メカニズムを落とす単純な代替は `dd_violation` とする
|
|
67
|
+
- 永続化・共有・外部から観測可能な状態への変更では、公開境界(新しい状態が別プロセス・コンポーネント・ユーザー・後続ステップから観測可能になる箇所)を特定する。部分的・未初期化・stale・ロールバックのみでありながら完了として観測可能な状態は `reliability` の検出事項とする。下流の利用者が不完全な状態を完了とみなして失敗しうるためである
|
|
65
68
|
|
|
66
69
|
#### 2-2. 識別子の検証
|
|
67
70
|
|
|
@@ -94,6 +94,12 @@ package.json の `packageManager` フィールドに従って実行コマンド
|
|
|
94
94
|
- 一致したのが (a+c) または (b+c) の組み合わせのみ → エスカレーション。その他の2項目組み合わせ → 継続実装
|
|
95
95
|
- 1項目以下一致 → 継続実装
|
|
96
96
|
|
|
97
|
+
### Step4: 中核メカニズム保全チェック(以下1つでもYES → 即エスカレーション)
|
|
98
|
+
タスク・AC・Design Doc・UI Spec が要求する中核メカニズムを保全する。実装詳細(変数名、内部のロジック順序、ローカルな構造)は自由に変更してよいが、要求された中核メカニズムそのものは保つ。
|
|
99
|
+
□ 要求された中核メカニズムを、より単純または弱い代替で置き換えようとしている?(テストが通ることは代替を正当化しない)
|
|
100
|
+
□ 要求された中核メカニズムが仕様どおりには実現不可能?
|
|
101
|
+
1つでもYES → 実装を中止し、`escalation_type: "design_compliance_violation"` でエスカレーション(エスカレーションレスポンス表に従い、ケースを契約フィールドに対応づける): `design_doc_expectation` = 要求された中核メカニズムと、その出所となる文言(task/AC/Design Doc/UI Spec); `actual_situation` = 提案された代替と、結果として生じる振る舞いの差分; `why_cannot_implement` = 中核メカニズムを置き換えた、または仕様どおりに実現できない理由; `attempted_approaches[]` = 中核メカニズムを保全するために試みた方法。実装前に実現不可能と判明している場合は `[]`; `claude_recommendation` = ブロックを解除する条件。
|
|
102
|
+
|
|
97
103
|
### 境界ケースと鉄則
|
|
98
104
|
|
|
99
105
|
| ケース | 継続 | エスカレーション |
|
|
@@ -105,7 +111,7 @@ package.json の `packageManager` フィールドに従って実行コマンド
|
|
|
105
111
|
|
|
106
112
|
**鉄則 — 客観的に判定不可のときはエスカレーション**: 判定項目について2通り以上の解釈が成り立つ; 過去の実装経験で遭遇していないパターン; 判定に必要な情報がDesign Docにない; 同等の技術者でも判断が分かれる。
|
|
107
113
|
|
|
108
|
-
### 継続実装可(Step1-
|
|
114
|
+
### 継続実装可(Step1-4 の全チェックが NO かつ明確に該当)
|
|
109
115
|
内部詳細の最適化(変数名、ロジック順序); Design Doc 未記載の詳細仕様; 外部 API レスポンス用の `unknown` → 具体型への安全な型ガード; 軽微なUI・メッセージ文言調整。
|
|
110
116
|
|
|
111
117
|
## 責務・権限・境界
|
|
@@ -94,6 +94,12 @@ package.json の `packageManager` フィールドに従って実行コマンド
|
|
|
94
94
|
- 一致したのが (a+c) または (b+c) の組み合わせのみ → エスカレーション。その他の2項目組み合わせ → 継続実装
|
|
95
95
|
- 1項目以下一致 → 継続実装
|
|
96
96
|
|
|
97
|
+
### Step4: 中核メカニズム保全チェック(以下1つでもYES → 即エスカレーション)
|
|
98
|
+
タスク・AC・Design Doc・参照資料が要求する中核メカニズムを保全する。実装詳細(変数名、内部の処理順序、ローカルな構造)は自由に変更してよいが、要求された中核メカニズムそのものは保つ。
|
|
99
|
+
□ 要求された中核メカニズムを、より単純または弱い代替で置き換えようとしている?(テストが通ることは代替を正当化しない)
|
|
100
|
+
□ 要求された中核メカニズムが仕様どおりには実現不可能?
|
|
101
|
+
1つでもYES → 実装を中止し、`escalation_type: "design_compliance_violation"` でエスカレーション(エスカレーションレスポンス表に従い、ケースを契約フィールドに対応づける): `design_doc_expectation` = 要求された中核メカニズムと、その出所となる文言(task/AC/Design Doc/参照資料); `actual_situation` = 提案された代替と、結果として生じる振る舞いの差分; `why_cannot_implement` = 中核メカニズムを置き換えた、または仕様どおりに実現できない理由; `attempted_approaches[]` = 中核メカニズムを保全するために試みた方法。実装前に実現不可能と判明している場合は `[]`; `claude_recommendation` = ブロックを解除する条件。
|
|
102
|
+
|
|
97
103
|
### 境界ケースと鉄則
|
|
98
104
|
|
|
99
105
|
| ケース | 継続 | エスカレーション |
|
|
@@ -105,7 +111,7 @@ package.json の `packageManager` フィールドに従って実行コマンド
|
|
|
105
111
|
|
|
106
112
|
**鉄則 — 客観的に判定不可のときはエスカレーション**: 判定項目について2通り以上の解釈が成り立つ; 過去の実装経験で遭遇していないパターン; 判定に必要な情報がDesign Docにない; 同等の技術者でも判断が分かれる。
|
|
107
113
|
|
|
108
|
-
### 継続実装可(Step1-
|
|
114
|
+
### 継続実装可(Step1-4 の全チェックが NO かつ明確に該当)
|
|
109
115
|
内部詳細の最適化(変数名、処理順序); Design Doc 未記載の詳細仕様; `unknown` → 具体型への安全な型ガード; 軽微なUI・メッセージ文言調整。
|
|
110
116
|
|
|
111
117
|
## 責務・権限・境界
|
|
@@ -30,29 +30,7 @@ ADR/Design Docの作成閾値はdocumentation-criteriaスキルに準拠。判
|
|
|
30
30
|
|
|
31
31
|
### ゲート順序 [BLOCKING]
|
|
32
32
|
|
|
33
|
-
以下のサブセクションは並列の必須事項ではなく、4
|
|
34
|
-
|
|
35
|
-
**Gate 0 — Inputs and Standards**(上流依存なし):
|
|
36
|
-
- Agreement Checklist
|
|
37
|
-
- Standards Identification
|
|
38
|
-
|
|
39
|
-
**Gate 1 — Existing State Analysis**(Gate 0 に依存):
|
|
40
|
-
- Existing Code Investigation
|
|
41
|
-
- Fact Disposition(Codebase Analysis 入力が提供される場合)
|
|
42
|
-
- Minimal Surface Alternatives(永続化されるクライアント/サーバー状態、所有境界を越える Props またはフィールド — エクスポートされた再利用可能コンポーネントの公開 API Props、Context 値、所有境界を越えて持ち上げられた状態 — 観測可能な振る舞いを変える振る舞いモード/バリアント、または再利用可能なコンポーネント分割を導入する場合)
|
|
43
|
-
|
|
44
|
-
**Gate 2 — Design Decisions**(Gate 1 に依存):
|
|
45
|
-
- Implementation Approach Decision
|
|
46
|
-
- Common ADR Process
|
|
47
|
-
- Data Contracts
|
|
48
|
-
- State Transitions(該当する場合)
|
|
49
|
-
|
|
50
|
-
**Gate 3 — Impact Documentation**(Gate 2 に依存):
|
|
51
|
-
- Integration Point Analysis
|
|
52
|
-
- Change Impact Map
|
|
53
|
-
- Interface Change Impact Analysis
|
|
54
|
-
|
|
55
|
-
各サブセクションには見出しに `[Gate N — ...]` の注記を付す。サブセクションはゲート順(Gate 0 → 1 → 2 → 3)で記載されており、文書順に実行すること。
|
|
33
|
+
以下のサブセクションは並列の必須事項ではなく、4段階の直列ゲートを構成する: **Gate 0** Inputs & Standards → **Gate 1** Existing-State Analysis → **Gate 2** Design Decisions → **Gate 3** Impact Documentation。各ゲートを完了してから次のゲートに進むこと。各サブセクションには見出しに `[Gate N — ...]` の注記(適用条件を含む)が付き、ゲート順に記載されている。文書順に実行すること。
|
|
56
34
|
|
|
57
35
|
### 合意事項チェックリスト [Gate 0 — Required]
|
|
58
36
|
Design Doc作成の最初に必ず実施:
|
|
@@ -331,31 +309,7 @@ UI Specが存在する場合(`docs/ui-spec/{feature-name}-ui-spec.md`):
|
|
|
331
309
|
|
|
332
310
|
**必須**: ADR/Design Doc 内の実装サンプルは frontend-typescript-rules スキルに必ず準拠すること。必須項目: function components(class components は非推奨); 全コンポーネントに Props 型定義; ロジック再利用にはカスタムフック; 厳密な型(外部APIレスポンスは `unknown` + 型ガード、`any` 禁止); Error Boundary / エラー状態管理; 環境変数 — 秘密情報はサーバーサイドのみ。
|
|
333
311
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
```typescript
|
|
337
|
-
type ButtonProps = { label: string; onClick: () => void; disabled?: boolean }
|
|
338
|
-
export function Button({ label, onClick, disabled = false }: ButtonProps) {
|
|
339
|
-
return <button onClick={onClick} disabled={disabled}>{label}</button>
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
function useUserData(userId: string) {
|
|
343
|
-
const [user, setUser] = useState<User | null>(null)
|
|
344
|
-
const [error, setError] = useState<Error | null>(null)
|
|
345
|
-
useEffect(() => {
|
|
346
|
-
void (async () => {
|
|
347
|
-
try {
|
|
348
|
-
const data: unknown = await (await fetch(`/api/users/${userId}`)).json()
|
|
349
|
-
if (!isUser(data)) throw new Error('Invalid user data')
|
|
350
|
-
setUser(data)
|
|
351
|
-
} catch (e) { setError(e instanceof Error ? e : new Error('Unknown error')) }
|
|
352
|
-
})()
|
|
353
|
-
}, [userId])
|
|
354
|
-
return { user, error }
|
|
355
|
-
}
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
非準拠: class components、`any`、ガードなし型なしレスポンス、クライアントサイドに埋め込まれた秘密情報。
|
|
312
|
+
非準拠: class components(Error Boundary を除く)、`any`、ガードなし型なしレスポンス、クライアントサイドに埋め込まれた秘密情報。
|
|
359
313
|
|
|
360
314
|
## 図表作成(mermaid)
|
|
361
315
|
|
|
@@ -394,6 +348,14 @@ function useUserData(userId: string) {
|
|
|
394
348
|
|
|
395
349
|
## 受け入れ基準作成ガイドライン
|
|
396
350
|
|
|
351
|
+
### 価値起点のドラフトと境界拡張
|
|
352
|
+
|
|
353
|
+
各ACは価値起点でドラフトし、下記のスコーピングルールを適用する前に要件境界へ展開する:
|
|
354
|
+
|
|
355
|
+
1. **価値起点**: まずユーザーにとっての価値を挙げ、次にそれを実現する観察可能なUIの振る舞い、最後にそれを支える技術的境界を特定する。
|
|
356
|
+
2. **境界への展開**(候補抽出 — 採否は下記のスコーピングルールが決定): ある振る舞いはハッピーパスでは成立しつつ、別の状態で退行しうる。振る舞いを変えるACごとに、約束した振る舞いが成立すべき箇所すべてでACを検討する — 単一/最新/全件のリスト描画、隣接する Props やフィールド、ローディング/空/エラーおよび後続のインタラクション状態、stale または欠損データ、フェッチ失敗やフォールバックUI、権限/バリデーションのゲーティング、入力スコープと順序/選択、副作用、可視性やルートの境界(状態が別画面・別コンポーネント・ナビゲーション後に観察可能になる箇所)。
|
|
357
|
+
3. **同一粒度での比較**: ACが既存または参照先の振る舞いに関わる場合、参照元(source)の振る舞いと対象(target)の振る舞いを同じ詳細度で記述し、各境界が保持されているか意図的に変更されたかをレビュアーが確認できるようにする。
|
|
358
|
+
|
|
397
359
|
### 自律実装のためのACスコーピング(フロントエンド)
|
|
398
360
|
|
|
399
361
|
**原則**: AC = 隔離されたCI環境でブラウザ上で検証可能なユーザー観察可能動作。ハッピーパス、アンハッピーパス(エラー)、エッジケース(空状態・ローディング状態)をカバー。重要なACを上位に配置し、非機能要件は別セクションで定義する。
|
|
@@ -29,31 +29,7 @@ ADR/Design Docの作成閾値はdocumentation-criteriaスキルに準拠。判
|
|
|
29
29
|
|
|
30
30
|
### ゲート順序 [BLOCKING]
|
|
31
31
|
|
|
32
|
-
以下のサブセクションは並列の必須事項ではなく、4
|
|
33
|
-
|
|
34
|
-
**Gate 0 — Inputs and Standards**(上流依存なし):
|
|
35
|
-
- Agreement Checklist
|
|
36
|
-
- Standards Identification
|
|
37
|
-
|
|
38
|
-
**Gate 1 — Existing State Analysis**(Gate 0 に依存):
|
|
39
|
-
- Existing Code Investigation
|
|
40
|
-
- Fact Disposition(Codebase Analysis 入力が提供される場合)
|
|
41
|
-
- Data Representation Decision(新規または変更されるデータ構造を導入する場合)
|
|
42
|
-
- Minimal Surface Alternatives(永続状態、公開コントラクト要素または境界を越えるフィールド、振る舞いモード/フラグ、再利用可能な抽象を導入する場合)
|
|
43
|
-
|
|
44
|
-
**Gate 2 — Design Decisions**(Gate 1 に依存):
|
|
45
|
-
- Implementation Approach Decision
|
|
46
|
-
- Common ADR Process
|
|
47
|
-
- Data Contracts
|
|
48
|
-
- State Transitions(該当する場合)
|
|
49
|
-
|
|
50
|
-
**Gate 3 — Impact Documentation**(Gate 2 に依存):
|
|
51
|
-
- Integration Points
|
|
52
|
-
- Change Impact Map
|
|
53
|
-
- Field Propagation Map(フィールドがコンポーネント境界を越える場合)
|
|
54
|
-
- Interface Change Impact Analysis
|
|
55
|
-
|
|
56
|
-
各サブセクションには見出しに `[Gate N — ...]` の注記を付す。サブセクションはゲート順(Gate 0 → 1 → 2 → 3)で記載されており、文書順に実行すること。
|
|
32
|
+
以下のサブセクションは並列の必須事項ではなく、4段階の直列ゲートを構成する: **Gate 0** Inputs & Standards → **Gate 1** Existing-State Analysis → **Gate 2** Design Decisions → **Gate 3** Impact Documentation。各ゲートを完了してから次のゲートに進むこと。各サブセクションには見出しに `[Gate N — ...]` の注記(適用条件を含む)が付き、ゲート順に記載されている。文書順に実行すること。
|
|
57
33
|
|
|
58
34
|
### 合意事項チェックリスト [Gate 0 — Required]
|
|
59
35
|
Design Doc作成の最初に必ず実施:
|
|
@@ -370,6 +346,14 @@ Design Doc作成時に必ず含める:
|
|
|
370
346
|
|
|
371
347
|
## 受け入れ基準作成ガイドライン
|
|
372
348
|
|
|
349
|
+
### 価値起点のドラフトと境界拡張
|
|
350
|
+
|
|
351
|
+
各ACは価値起点でドラフトし、下記のルールを適用する前に要件境界へ展開する:
|
|
352
|
+
|
|
353
|
+
1. **価値起点**: まずユーザー/オペレーター/保守者にとっての価値を挙げ、次にそれを実現する観測可能な振る舞い、最後にそれを支える技術的境界を特定する。
|
|
354
|
+
2. **境界への展開**(候補抽出 — 採否は下記ルールが決定): ある振る舞いはメインパスでは成立しつつ、別の次元で退行しうる。振る舞いを変えるACごとに、約束した振る舞いが成立すべき箇所すべてでACを検討する — 単一/最新/全件のコレクション、同一サーフェス上の隣接フィールド、後続のライフサイクル状態やリトライ、stale/欠損/空の値、リフレッシュ失敗やフォールバック不在、権限/バリデーション/ポリシーの境界、入力スコープと選択/順序/識別キー、副作用、公開・可視性の境界(状態が別プロセス・コンポーネント・ユーザー・後続ステップから観測可能になる箇所)。
|
|
355
|
+
3. **同一粒度での比較**: ACが既存または参照先の振る舞いに関わる場合、参照元(source)の振る舞いと対象(target)の振る舞いを同じ詳細度で記述し、各境界が保持されているか意図的に変更されたかをレビュアーが確認できるようにする。
|
|
356
|
+
|
|
373
357
|
### 測定可能なACの書き方
|
|
374
358
|
|
|
375
359
|
**原則**: AC = 独立した環境で検証可能かつユーザーが観測可能な振る舞い。正常系・異常系・エッジケースをカバー。非機能要件(パフォーマンス、信頼性、スケーラビリティ)は別の「非機能要件」セクションで定義する。
|
|
@@ -17,10 +17,10 @@ TypeScript-based React application implementation. Architecture patterns should
|
|
|
17
17
|
- Properly implement default value settings and mandatory checks
|
|
18
18
|
|
|
19
19
|
```typescript
|
|
20
|
-
// Build tool environment variables (public values only)
|
|
20
|
+
// Build tool environment variables (public values only; client-exposed vars need the VITE_ prefix)
|
|
21
21
|
const config = {
|
|
22
|
-
apiUrl: import.meta.env.
|
|
23
|
-
appName: import.meta.env.
|
|
22
|
+
apiUrl: import.meta.env.VITE_API_URL || 'http://localhost:3000',
|
|
23
|
+
appName: import.meta.env.VITE_APP_NAME || 'My App'
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
// Does not work in frontend
|
|
@@ -37,7 +37,7 @@ const apiUrl = process.env.API_URL
|
|
|
37
37
|
**Correct Approach for Secrets**:
|
|
38
38
|
```typescript
|
|
39
39
|
// Security risk: API key exposed in browser
|
|
40
|
-
const apiKey = import.meta.env.
|
|
40
|
+
const apiKey = import.meta.env.VITE_API_KEY
|
|
41
41
|
const response = await fetch(`https://api.example.com/data?key=${apiKey}`)
|
|
42
42
|
|
|
43
43
|
// Correct: Backend manages secrets, frontend accesses via proxy
|
|
@@ -5,132 +5,66 @@ description: Applies React/TypeScript type safety, component design, and state m
|
|
|
5
5
|
|
|
6
6
|
# TypeScript Development Rules (Frontend)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Frontend-specific React/TypeScript rules for implementation: thresholds, boundary type safety, component/state design, error handling, and project conventions.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
## Anti-patterns and Thresholds
|
|
11
|
+
Signals that trigger a design change:
|
|
12
|
+
- Prop drilling through 3+ levels → lift to Context or state management
|
|
13
|
+
- Component over 300 lines → split
|
|
14
|
+
- Props count over 10 → split the component (3-7 is the working range)
|
|
15
|
+
- Optional props over 50% → introduce defaults or Context
|
|
16
|
+
- Props nesting deeper than 2 levels → flatten
|
|
17
|
+
- The same `as` assertion appearing 3+ times → revisit the type design
|
|
11
18
|
|
|
12
|
-
|
|
13
|
-
|
|
19
|
+
## Type Safety at Boundaries
|
|
20
|
+
Prohibit `any`; when a type is unavailable, receive it as `unknown` and narrow with a type guard. Minimize `as` (justify with a comment when unavoidable).
|
|
14
21
|
|
|
15
|
-
|
|
22
|
+
Inside the app, React Props/State are type-guaranteed — no `unknown` needed. At every external boundary, receive as `unknown` and narrow with a type guard before use: API responses, `localStorage`/`sessionStorage`, URL parameters, parsed JSON. Controlled-component form input stays type-safe through React synthetic events.
|
|
16
23
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
- **React Props/State**: TypeScript manages types, unknown unnecessary
|
|
23
|
-
- **External API Responses**: Always receive as `unknown`, validate with type guards
|
|
24
|
-
- **localStorage/sessionStorage**: Treat as `unknown`, validate
|
|
25
|
-
- **URL Parameters**: Treat as `unknown`, validate
|
|
26
|
-
- **Form Input (Controlled Components)**: Type-safe with React synthetic events
|
|
27
|
-
|
|
28
|
-
**Type Complexity Management (Frontend)**
|
|
29
|
-
- **Props Design**:
|
|
30
|
-
- Props count: 3-7 props ideal (consider component splitting if exceeds 10)
|
|
31
|
-
- Optional Props: 50% or less (consider default values or Context if excessive)
|
|
32
|
-
- Nesting: Up to 2 levels (flatten deeper structures)
|
|
33
|
-
- Type Assertions: Review design if used 3+ times
|
|
34
|
-
- **External API Types**: Relax constraints and define according to reality (convert appropriately internally)
|
|
35
|
-
|
|
36
|
-
## Coding Conventions
|
|
37
|
-
|
|
38
|
-
**Component Design Criteria**
|
|
39
|
-
- **Function Components (Mandatory)**: Official React recommendation, optimizable by modern tooling
|
|
40
|
-
- **Classes Prohibited**: Class components completely deprecated (Exception: Error Boundary)
|
|
41
|
-
- **Custom Hooks**: Standard pattern for logic reuse
|
|
42
|
-
|
|
43
|
-
**Function Design**
|
|
44
|
-
- **0-2 parameters maximum**: Use object for 3+ parameters
|
|
45
|
-
```typescript
|
|
46
|
-
// Object parameter
|
|
47
|
-
function createUser({ name, email, role }: CreateUserParams) {}
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
**Props Design (Props-driven Approach)**
|
|
51
|
-
- Props are the interface: Define all necessary information as props
|
|
52
|
-
- Avoid implicit dependencies: Do not depend on global state or context without necessity
|
|
53
|
-
- Type-safe: Always define Props type explicitly
|
|
54
|
-
|
|
55
|
-
**Environment Variables**
|
|
56
|
-
- **Use build tool's environment variable system**: `process.env` does not work in browsers
|
|
57
|
-
- **No secrets on client-side**: All frontend code is public, manage secrets in backend
|
|
58
|
-
|
|
59
|
-
**Dependency Injection**
|
|
60
|
-
- **Custom Hooks for dependency injection**: Ensure testability and modularity
|
|
61
|
-
|
|
62
|
-
**Asynchronous Processing**
|
|
63
|
-
- Promise Handling: Always use `async/await`
|
|
64
|
-
- Error Handling: Always handle with `try-catch` or Error Boundary
|
|
65
|
-
- Type Definition: Explicitly define return value types (e.g., `Promise<Result>`)
|
|
66
|
-
|
|
67
|
-
**Format Rules**
|
|
68
|
-
- Semicolon omission (follow Biome settings)
|
|
69
|
-
- Types in `PascalCase`, variables/functions in `camelCase`
|
|
70
|
-
- Imports use absolute paths (`src/`)
|
|
24
|
+
```typescript
|
|
25
|
+
const raw: unknown = await (await fetch(url)).json()
|
|
26
|
+
if (!isUser(raw)) throw new ValidationError('invalid user')
|
|
27
|
+
const user = raw // narrowed to User
|
|
28
|
+
```
|
|
71
29
|
|
|
72
|
-
|
|
73
|
-
-
|
|
74
|
-
-
|
|
75
|
-
-
|
|
76
|
-
-
|
|
30
|
+
## Component and State Design
|
|
31
|
+
- **Function components only.** Class components are allowed solely for Error Boundaries (no hook equivalent exists).
|
|
32
|
+
- **Type Props explicitly** with a named type and destructure: `function UserCard({ user, onSelect }: UserCardProps)`. Avoid `React.FC`; type props directly on the function so the props contract stays explicit.
|
|
33
|
+
- **Props-driven:** pass dependencies as props; reach into global state or Context only when needed.
|
|
34
|
+
- **Custom hooks** are the unit of logic reuse and dependency injection (inject collaborators through the hook for testability).
|
|
35
|
+
- **Function parameters:** 0-2 positional; for 3+ take a single options object.
|
|
36
|
+
- **State shape:** type state explicitly; for multi-field state with discrete transitions, use `useReducer` with a discriminated-union action type rather than many `useState` calls.
|
|
77
37
|
|
|
78
38
|
## Error Handling
|
|
39
|
+
- Surface every error: log and handle, or propagate — never swallow.
|
|
40
|
+
- **Fail fast:** on an invalid state, throw rather than returning a silent fallback.
|
|
41
|
+
- Represent expected failures as values with a `Result` type; reserve `throw` for unexpected/unrecoverable cases.
|
|
42
|
+
- Use purpose-specific error classes extending a base `AppError` carrying a `code` (e.g. ValidationError, ApiError, NotFoundError).
|
|
43
|
+
- **Layer responsibilities:** the API layer converts transport errors into domain errors; hooks propagate `AppError` upward; an Error Boundary catches render-time errors and shows fallback UI.
|
|
44
|
+
- Never log secrets (password, token, apiKey, creditCard).
|
|
79
45
|
|
|
80
|
-
**Absolute Rule**: Error suppression prohibited. All errors must have log output and appropriate handling.
|
|
81
|
-
|
|
82
|
-
**Fail-Fast Principle**: Fail quickly on errors to prevent continued processing in invalid states
|
|
83
|
-
```typescript
|
|
84
|
-
// Prohibited: Unconditional fallback
|
|
85
|
-
catch (error) {
|
|
86
|
-
return defaultValue // Hides error
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Required: Explicit failure
|
|
90
|
-
catch (error) {
|
|
91
|
-
logger.error('Processing failed', error)
|
|
92
|
-
throw error // Handle with Error Boundary or higher layer
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
**Result Type Pattern**: Express errors with types for explicit handling
|
|
97
46
|
```typescript
|
|
98
47
|
type Result<T, E> = { ok: true; value: T } | { ok: false; error: E }
|
|
99
48
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
49
|
+
class AppError extends Error {
|
|
50
|
+
constructor(message: string, readonly code: string, readonly statusCode = 500) {
|
|
51
|
+
super(message); this.name = this.constructor.name
|
|
52
|
+
}
|
|
104
53
|
}
|
|
105
54
|
```
|
|
106
55
|
|
|
107
|
-
|
|
56
|
+
Error Boundary — the one place a class component is required:
|
|
108
57
|
```typescript
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
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 }
|
|
114
62
|
}
|
|
115
|
-
// Purpose-specific: ValidationError(400), ApiError(502), NotFoundError(404)
|
|
116
63
|
```
|
|
117
64
|
|
|
118
|
-
|
|
119
|
-
-
|
|
120
|
-
-
|
|
121
|
-
-
|
|
122
|
-
|
|
123
|
-
**
|
|
124
|
-
Never include sensitive information (password, token, apiKey, secret, creditCard) in logs
|
|
125
|
-
|
|
126
|
-
**Asynchronous Error Handling in React**
|
|
127
|
-
- Error Boundary setup mandatory: Catch rendering errors
|
|
128
|
-
- Use try-catch with all async/await in event handlers
|
|
129
|
-
- Always log and re-throw errors or display error state
|
|
130
|
-
|
|
131
|
-
## Performance Optimization
|
|
132
|
-
|
|
133
|
-
- Component Memoization: Use React.memo for expensive components
|
|
134
|
-
- State Optimization: Minimize re-renders with proper state structure
|
|
135
|
-
- Lazy Loading: Use React.lazy and Suspense for code splitting
|
|
136
|
-
- Bundle Size: Monitor with the `build` script and keep under 500KB
|
|
65
|
+
## Project Conventions
|
|
66
|
+
- **Environment variables:** read through the build tool's env system (`process.env` is absent in the browser). Keep all secrets server-side — frontend code ships to the client.
|
|
67
|
+
- **Bundle & performance:** monitor with the `build` script, keep under 500KB; memoize expensive components (`React.memo`); code-split with `React.lazy` + `Suspense`; structure state to minimize re-renders.
|
|
68
|
+
- **Naming:** components/types `PascalCase`; variables/functions `camelCase`; hooks `use`-prefixed; constants `SCREAMING_SNAKE_CASE`.
|
|
69
|
+
- **Imports:** absolute paths from `src/`; order: React → external libs → internal (absolute) → internal (relative) → type-only → styles/assets.
|
|
70
|
+
- **Formatting:** follow Biome (semicolons and style come from project config).
|
|
@@ -140,8 +140,10 @@ describe('Button', () => {
|
|
|
140
140
|
|
|
141
141
|
## Test Design Patterns
|
|
142
142
|
|
|
143
|
+
Test user-visible results, not implementation details. Query by accessibility (`getByRole`/`getByLabelText`/`getByText`), not `getByTestId` or `container.querySelector`. Cover empty, error, and loading/async states, not only the happy path; await async UI with `findBy*`.
|
|
144
|
+
|
|
143
145
|
```typescript
|
|
144
|
-
//
|
|
146
|
+
// Test the user-visible result
|
|
145
147
|
it('increments count when clicked', async () => {
|
|
146
148
|
const user = userEvent.setup()
|
|
147
149
|
render(<Counter />)
|
|
@@ -149,10 +151,10 @@ it('increments count when clicked', async () => {
|
|
|
149
151
|
expect(screen.getByText('Count: 1')).toBeInTheDocument()
|
|
150
152
|
})
|
|
151
153
|
|
|
152
|
-
//
|
|
153
|
-
it('
|
|
154
|
-
|
|
155
|
-
render(<
|
|
156
|
-
|
|
154
|
+
// Error state: override the handler for one test
|
|
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('Something went wrong')).toBeInTheDocument()
|
|
157
159
|
})
|
|
158
160
|
```
|
|
@@ -192,21 +192,19 @@ skills:
|
|
|
192
192
|
# Frontend-specific Skills
|
|
193
193
|
frontend-typescript-rules:
|
|
194
194
|
skill: "frontend-typescript-rules"
|
|
195
|
-
tags: [frontend, react, implementation, type-safety,
|
|
196
|
-
typical-use: "React component creation, Props
|
|
195
|
+
tags: [frontend, react, implementation, type-safety, component-design, props-driven, hooks, error-handling, conventions]
|
|
196
|
+
typical-use: "React component creation, Props/state design, error handling, frontend TypeScript implementation rules"
|
|
197
197
|
size: small
|
|
198
198
|
key-references:
|
|
199
|
-
- "React Function Components - React
|
|
200
|
-
- "
|
|
201
|
-
- "
|
|
202
|
-
- "Clean Code - Robert C. Martin"
|
|
203
|
-
- "TypeScript satisfies Operator - Microsoft"
|
|
199
|
+
- "React Function Components - React docs"
|
|
200
|
+
- "Type guards at external boundaries (unknown narrowing)"
|
|
201
|
+
- "Result type and Error Boundary error handling"
|
|
204
202
|
sections:
|
|
205
|
-
- "
|
|
206
|
-
- "Type Safety
|
|
207
|
-
- "
|
|
203
|
+
- "Anti-patterns and Thresholds"
|
|
204
|
+
- "Type Safety at Boundaries"
|
|
205
|
+
- "Component and State Design"
|
|
208
206
|
- "Error Handling"
|
|
209
|
-
- "
|
|
207
|
+
- "Project Conventions"
|
|
210
208
|
|
|
211
209
|
frontend-typescript-testing:
|
|
212
210
|
skill: "frontend-typescript-testing"
|
|
@@ -17,10 +17,10 @@ TypeScriptベースのReactアプリケーション実装。アーキテクチ
|
|
|
17
17
|
- デフォルト値設定と必須チェックの適切な実装
|
|
18
18
|
|
|
19
19
|
```typescript
|
|
20
|
-
//
|
|
20
|
+
// ビルドツールの環境変数(公開値のみ。クライアント公開変数は VITE_ 接頭辞が必要)
|
|
21
21
|
const config = {
|
|
22
|
-
apiUrl: import.meta.env.
|
|
23
|
-
appName: import.meta.env.
|
|
22
|
+
apiUrl: import.meta.env.VITE_API_URL || 'http://localhost:3000',
|
|
23
|
+
appName: import.meta.env.VITE_APP_NAME || 'My App'
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
// フロントエンドでは動作しない
|
|
@@ -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
|
```
|
|
@@ -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,17 @@ 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
|
+
|
|
8
19
|
## [1.23.2] - 2026-06-01
|
|
9
20
|
|
|
10
21
|
### 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": [
|