Wave 0 TDD stubs, Wave 1 parallel (registry + ReviewStep), Wave 2 act() fix. Covers TECH-01 through TECH-05. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
249 lines
11 KiB
Markdown
249 lines
11 KiB
Markdown
---
|
|
phase: 05-tech-debt
|
|
plan: "01"
|
|
type: execute
|
|
wave: 1
|
|
depends_on:
|
|
- "05-00"
|
|
files_modified:
|
|
- src/schemas/registry.ts
|
|
- src/schemas/index.ts
|
|
- src/components/wizard/RemoteConfigStep.tsx
|
|
- src/components/wizard/BackendSelectionStep.tsx
|
|
autonomous: true
|
|
requirements:
|
|
- TECH-03
|
|
- TECH-04
|
|
|
|
must_haves:
|
|
truths:
|
|
- "BACKEND_REGISTRY entries each have displayName, description, and fields properties"
|
|
- "BackendSelectionStep renders cards using Object.entries(BACKEND_REGISTRY) — no hardcoded BACKENDS array"
|
|
- "All consumers of BACKEND_REGISTRY access field arrays via .fields"
|
|
- "BackendFormValues<T> export is absent from src/schemas/index.ts"
|
|
- "TypeScript compiles with zero errors after changes"
|
|
artifacts:
|
|
- path: "src/schemas/registry.ts"
|
|
provides: "Enriched BACKEND_REGISTRY with displayName, description, fields"
|
|
contains: "displayName"
|
|
- path: "src/components/wizard/BackendSelectionStep.tsx"
|
|
provides: "Registry-driven card list"
|
|
contains: "Object.entries(BACKEND_REGISTRY)"
|
|
key_links:
|
|
- from: "src/schemas/registry.ts"
|
|
to: "src/schemas/index.ts"
|
|
via: "buildZodSchema accesses BACKEND_REGISTRY[backendType].fields"
|
|
pattern: "\\.fields"
|
|
- from: "src/schemas/registry.ts"
|
|
to: "src/components/wizard/RemoteConfigStep.tsx"
|
|
via: "field iteration uses BACKEND_REGISTRY[backendType].fields"
|
|
pattern: "BACKEND_REGISTRY\\[backendType\\]\\.fields"
|
|
- from: "src/schemas/registry.ts"
|
|
to: "src/components/wizard/BackendSelectionStep.tsx"
|
|
via: "Object.entries(BACKEND_REGISTRY) replaces BACKENDS const"
|
|
pattern: "Object\\.entries\\(BACKEND_REGISTRY\\)"
|
|
---
|
|
|
|
<objective>
|
|
Enrich BACKEND_REGISTRY with display metadata, wire BackendSelectionStep to derive its card list from the registry, update all consumers to use .fields access, and remove the dead BackendFormValues<T> export.
|
|
|
|
Purpose: TECH-03 makes the registry the single source of truth for both field definitions AND display metadata — Phase 6 backend additions auto-surface with zero additional UI code. TECH-04 removes dead code.
|
|
Output: registry.ts with new shape, BackendSelectionStep.tsx driven by registry, schemas/index.ts clean.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md
|
|
@C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.planning/PROJECT.md
|
|
@.planning/ROADMAP.md
|
|
@.planning/phases/05-tech-debt/05-CONTEXT.md
|
|
@.planning/phases/05-tech-debt/05-RESEARCH.md
|
|
@.planning/phases/05-tech-debt/05-00-SUMMARY.md
|
|
|
|
<interfaces>
|
|
<!-- Extracted from codebase — executor uses these directly -->
|
|
|
|
From src/schemas/registry.ts (CURRENT shape — executor REPLACES this):
|
|
```typescript
|
|
export type BackendType = 'azureblob' | 's3' | 's3-compatible';
|
|
export interface FieldDef { key, label, inputType, required, placeholder?, helpText?, options? }
|
|
export const BACKEND_REGISTRY: Record<BackendType, FieldDef[]> = { ... }
|
|
```
|
|
|
|
New shape (TECH-03 target):
|
|
```typescript
|
|
export const BACKEND_REGISTRY: Record<BackendType, {
|
|
displayName: string;
|
|
description: string;
|
|
fields: FieldDef[];
|
|
}> = {
|
|
azureblob: {
|
|
displayName: 'Azure Blob Storage',
|
|
description: 'Microsoft Azure cloud storage',
|
|
fields: [ /* existing FieldDef[] content unchanged */ ],
|
|
},
|
|
s3: {
|
|
displayName: 'Amazon S3',
|
|
description: 'AWS Simple Storage Service',
|
|
fields: [ /* existing FieldDef[] content unchanged */ ],
|
|
},
|
|
's3-compatible': {
|
|
displayName: 'S3-Compatible',
|
|
description: 'Wasabi, MinIO, Cloudflare R2, and others',
|
|
fields: [ /* existing FieldDef[] content unchanged */ ],
|
|
},
|
|
};
|
|
```
|
|
|
|
Display strings MUST match existing BackendSelectionStep card text exactly (test assertions check these strings).
|
|
|
|
From src/schemas/index.ts line 10 (consumer 1 — must update):
|
|
```typescript
|
|
const fields = BACKEND_REGISTRY[backendType]; // CHANGE TO: BACKEND_REGISTRY[backendType].fields
|
|
```
|
|
|
|
From src/components/wizard/RemoteConfigStep.tsx (consumers 2 and 3 — must update):
|
|
```typescript
|
|
// Line 58: BACKEND_REGISTRY.azureblob.find(f => f.key === 'account')
|
|
// CHANGE TO: BACKEND_REGISTRY.azureblob.fields.find(f => f.key === 'account')
|
|
// Line 73: BACKEND_REGISTRY[backendType].map(field => ...)
|
|
// CHANGE TO: BACKEND_REGISTRY[backendType].fields.map(field => ...)
|
|
```
|
|
|
|
From src/components/wizard/BackendSelectionStep.tsx (TECH-03 — replace hardcoded BACKENDS):
|
|
```typescript
|
|
// Remove: const BACKENDS: { type: BackendType; name: string; description: string }[] = [...]
|
|
// Replace rendering with:
|
|
import { BACKEND_REGISTRY } from '../../schemas/registry';
|
|
// ...
|
|
{Object.entries(BACKEND_REGISTRY).map(([type, entry]) => (
|
|
<BackendCard
|
|
key={type}
|
|
name={entry.displayName}
|
|
description={entry.description}
|
|
selected={state.remote.backendType === type}
|
|
onClick={() => handleCardClick(type as BackendType)}
|
|
/>
|
|
))}
|
|
```
|
|
|
|
From src/schemas/index.ts lines 27-28 (TECH-04 — remove entirely):
|
|
```typescript
|
|
// REMOVE these two lines:
|
|
export type BackendFormValues<T extends BackendType> =
|
|
z.infer<typeof BACKEND_SCHEMAS[T]>;
|
|
```
|
|
</interfaces>
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 1: Enrich BACKEND_REGISTRY and update all consumers</name>
|
|
<files>src/schemas/registry.ts, src/schemas/index.ts, src/components/wizard/RemoteConfigStep.tsx</files>
|
|
<behavior>
|
|
- registry.test.ts 'each backend entry has displayName and description metadata' passes
|
|
- registry.test.ts 'each backend has at least one field definition' passes (via .fields.length)
|
|
- registry.test.ts 'Azure Blob has account field' passes (via .fields.find)
|
|
- schemas/index.ts buildZodSchema receives FieldDef[] from .fields — all BACKEND_SCHEMAS build correctly
|
|
- RemoteConfigStep renders fields correctly (existing RemoteConfigStep.test.tsx passes)
|
|
</behavior>
|
|
<action>
|
|
1. Edit src/schemas/registry.ts:
|
|
- Change BACKEND_REGISTRY value type from `FieldDef[]` to `{ displayName: string; description: string; fields: FieldDef[] }`
|
|
- Wrap each backend's existing FieldDef array in the new object shape, adding displayName and description
|
|
- displayName and description strings MUST match the existing BACKENDS array in BackendSelectionStep.tsx exactly:
|
|
- azureblob: displayName='Azure Blob Storage', description='Microsoft Azure cloud storage'
|
|
- s3: displayName='Amazon S3', description='AWS Simple Storage Service'
|
|
- s3-compatible: displayName='S3-Compatible', description='Wasabi, MinIO, Cloudflare R2, and others'
|
|
- All existing FieldDef content is preserved unchanged inside fields
|
|
|
|
2. Edit src/schemas/index.ts line 10:
|
|
- Change `const fields = BACKEND_REGISTRY[backendType];` to `const fields = BACKEND_REGISTRY[backendType].fields;`
|
|
|
|
3. Edit src/components/wizard/RemoteConfigStep.tsx:
|
|
- Line 58: Change `BACKEND_REGISTRY.azureblob.find(f => f.key === 'account')` to `BACKEND_REGISTRY.azureblob.fields.find(f => f.key === 'account')`
|
|
- Line 73: Change `BACKEND_REGISTRY[backendType].map(field =>` to `BACKEND_REGISTRY[backendType].fields.map(field =>`
|
|
</action>
|
|
<verify>
|
|
<automated>cd /c/Users/SebastienQUEROL/Documents/projets/Ready2Blob && npx vitest run src/schemas/registry.test.ts src/components/wizard/RemoteConfigStep.test.tsx 2>&1 | tail -15</automated>
|
|
</verify>
|
|
<done>
|
|
registry.test.ts passes with all tests green (including new displayName/description test and .fields access tests).
|
|
RemoteConfigStep.test.tsx still passes.
|
|
TypeScript sees no errors on the modified files.
|
|
</done>
|
|
</task>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 2: Wire BackendSelectionStep to registry + remove dead export</name>
|
|
<files>src/components/wizard/BackendSelectionStep.tsx, src/schemas/index.ts</files>
|
|
<behavior>
|
|
- BackendSelectionStep renders 'Azure Blob Storage', 'Amazon S3', 'S3-Compatible' cards (from registry, not hardcoded)
|
|
- Azure Blob card appears before S3 in DOM order (Object.entries insertion order preserved)
|
|
- Clicking a card with a valid remote name still dispatches SET_BACKEND_TYPE and SET_STEP (existing WIZD-01 test passes)
|
|
- BackendFormValues<T> type is absent from src/schemas/index.ts
|
|
- npx tsc --noEmit exits with code 0
|
|
</behavior>
|
|
<action>
|
|
1. Edit src/components/wizard/BackendSelectionStep.tsx:
|
|
- Add import: `import { BACKEND_REGISTRY } from '../../schemas/registry';`
|
|
- Remove the `BACKENDS` constant entirely (lines 21-37)
|
|
- In the JSX rendering section, replace `{BACKENDS.map((backend) => (` block with:
|
|
```tsx
|
|
{Object.entries(BACKEND_REGISTRY).map(([type, entry]) => (
|
|
<BackendCard
|
|
key={type}
|
|
name={entry.displayName}
|
|
description={entry.description}
|
|
selected={state.remote.backendType === type}
|
|
onClick={() => handleCardClick(type as BackendType)}
|
|
/>
|
|
))}
|
|
```
|
|
- Remove the now-unused local type import for BackendType from '../../store/types' IF it is only used by the BACKENDS const (check — it may still be needed for pendingBackend.current type annotation). If BackendType is still needed, keep the import.
|
|
|
|
2. Edit src/schemas/index.ts:
|
|
- Remove lines 27-28: the `export type BackendFormValues<T extends BackendType> = z.infer<typeof BACKEND_SCHEMAS[T]>;` export
|
|
- Also remove the blank comment line above it (line 26: `// Utility type: infer TypeScript type from a backend's Zod schema`) to avoid orphan comment
|
|
|
|
3. Run TypeScript check to confirm no consumers of BackendFormValues exist:
|
|
`npx tsc --noEmit`
|
|
</action>
|
|
<verify>
|
|
<automated>cd /c/Users/SebastienQUEROL/Documents/projets/Ready2Blob && npx vitest run src/components/wizard/BackendSelectionStep.test.tsx && npx tsc --noEmit 2>&1 | tail -10</automated>
|
|
</verify>
|
|
<done>
|
|
BackendSelectionStep.test.tsx passes (WIZD-01 card text assertions still green — display strings match).
|
|
npx tsc --noEmit exits with zero errors.
|
|
BackendFormValues export is absent from src/schemas/index.ts.
|
|
</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
Full suite green except pre-existing RED stubs (TECH-01, TECH-02 ReviewStep stubs remain RED — implemented in Plan 02):
|
|
```
|
|
cd /c/Users/SebastienQUEROL/Documents/projets/Ready2Blob && npx vitest run && npx tsc --noEmit
|
|
```
|
|
Expected: All registry tests green, all BackendSelectionStep tests green, RemoteConfigStep tests green. Only TECH-01/TECH-02 ReviewStep stubs still RED.
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- BACKEND_REGISTRY shape is `Record<BackendType, { displayName, description, fields: FieldDef[] }>`
|
|
- BackendSelectionStep uses `Object.entries(BACKEND_REGISTRY)` — no hardcoded BACKENDS array
|
|
- schemas/index.ts uses `.fields` access and has no BackendFormValues export
|
|
- RemoteConfigStep uses `.fields` access
|
|
- registry.test.ts fully green
|
|
- BackendSelectionStep.test.tsx fully green
|
|
- npx tsc --noEmit: zero errors
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/05-tech-debt/05-01-SUMMARY.md`
|
|
</output>
|