docs(01-foundation): create phase plan
4 plans across 3 waves: scaffold, registry+test stubs, Zod schemas and WizardState store (parallel wave 3).
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
---
|
||||
phase: 01-foundation
|
||||
plan: 03
|
||||
type: execute
|
||||
wave: 3
|
||||
depends_on:
|
||||
- 01-02
|
||||
files_modified:
|
||||
- src/schemas/index.ts
|
||||
autonomous: true
|
||||
requirements: []
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "BACKEND_SCHEMAS['azureblob'].safeParse({account: 'x'}) returns success: true"
|
||||
- "BACKEND_SCHEMAS['azureblob'].safeParse({account: ''}) returns success: false"
|
||||
- "BACKEND_SCHEMAS['s3-compatible'].safeParse({...without endpoint}) returns success: false"
|
||||
- "Zod schemas are derived programmatically from BACKEND_REGISTRY — no hand-written z.object() calls"
|
||||
- "All 12 tests in src/schemas/index.test.ts pass"
|
||||
artifacts:
|
||||
- path: "src/schemas/index.ts"
|
||||
provides: "BACKEND_SCHEMAS constant and BackendFormValues utility type"
|
||||
exports: ["BACKEND_SCHEMAS", "BackendFormValues"]
|
||||
key_links:
|
||||
- from: "src/schemas/index.ts"
|
||||
to: "src/schemas/registry.ts"
|
||||
via: "imports BACKEND_REGISTRY and BackendType to build schemas programmatically"
|
||||
pattern: "import.*BACKEND_REGISTRY.*registry"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Implement the Zod schema builder that derives runtime validation schemas programmatically from the Backend Schema Registry.
|
||||
|
||||
Purpose: Schema and registry must never drift — if someone adds a field to the registry, validation automatically covers it. Writing Zod schemas by hand separate from the registry breaks this invariant.
|
||||
Output: src/schemas/index.ts with BACKEND_SCHEMAS and BackendFormValues type — all 12 tests in src/schemas/index.test.ts pass.
|
||||
</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/phases/01-foundation/01-02-SUMMARY.md
|
||||
|
||||
<interfaces>
|
||||
<!-- From src/schemas/registry.ts (created in Plan 02) -->
|
||||
```typescript
|
||||
export type BackendType = 'azureblob' | 's3' | 's3-compatible';
|
||||
|
||||
export interface FieldDef {
|
||||
key: string;
|
||||
label: string;
|
||||
inputType: 'text' | 'password' | 'select' | 'toggle';
|
||||
required: boolean;
|
||||
placeholder?: string;
|
||||
helpText?: string;
|
||||
options?: { value: string; label: string }[];
|
||||
}
|
||||
|
||||
export const BACKEND_REGISTRY: Record<BackendType, FieldDef[]>;
|
||||
// azureblob fields: account (required), key (optional), sas_url (optional)
|
||||
// s3 fields: provider (required), access_key_id (required), secret_access_key (required), region (required)
|
||||
// s3-compatible fields: provider (required), access_key_id (required), secret_access_key (required), endpoint (required), region (optional)
|
||||
```
|
||||
|
||||
<!-- Test expectations from src/schemas/index.test.ts (created in Plan 02) -->
|
||||
<!-- BACKEND_SCHEMAS.azureblob.safeParse({account: 'x'}) → success: true -->
|
||||
<!-- BACKEND_SCHEMAS.azureblob.safeParse({account: ''}) → success: false -->
|
||||
<!-- BACKEND_SCHEMAS.azureblob.safeParse({key: 'x'}) (no account) → success: false -->
|
||||
<!-- BACKEND_SCHEMAS.s3.safeParse({provider,access_key_id,secret_access_key,region}) → success: true -->
|
||||
<!-- BACKEND_SCHEMAS.s3.safeParse({...missing access_key_id}) → success: false -->
|
||||
<!-- BACKEND_SCHEMAS['s3-compatible'].safeParse({...with endpoint}) → success: true -->
|
||||
<!-- BACKEND_SCHEMAS['s3-compatible'].safeParse({...without endpoint}) → success: false -->
|
||||
</interfaces>
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 1: Implement schema builder and BACKEND_SCHEMAS</name>
|
||||
<files>src/schemas/index.ts</files>
|
||||
<behavior>
|
||||
- buildZodSchema(backendType) constructs a z.object() from BACKEND_REGISTRY[backendType]
|
||||
- required fields → z.string().min(1, '{label} is required')
|
||||
- optional fields → z.string().optional()
|
||||
- BACKEND_SCHEMAS is a const object keyed by BackendType
|
||||
- BackendFormValues<T> infers the TypeScript type from the schema using z.infer
|
||||
- No z.object() calls hardcoded with field names — all field shapes derived from the registry loop
|
||||
</behavior>
|
||||
<action>
|
||||
Run the failing test first to confirm RED state:
|
||||
```
|
||||
npx vitest run src/schemas/index.test.ts
|
||||
```
|
||||
Expected: "Cannot find module './index'" error.
|
||||
|
||||
Create src/schemas/index.ts:
|
||||
|
||||
```typescript
|
||||
// src/schemas/index.ts
|
||||
// Zod schemas derived programmatically from the Backend Schema Registry.
|
||||
// DO NOT hand-write z.object() calls with hardcoded field names — all shapes come from the registry.
|
||||
// Adding a field to BACKEND_REGISTRY automatically adds it to validation.
|
||||
|
||||
import { z } from 'zod';
|
||||
import { BACKEND_REGISTRY, BackendType } from './registry';
|
||||
|
||||
function buildZodSchema(backendType: BackendType): z.ZodObject<Record<string, z.ZodTypeAny>> {
|
||||
const fields = BACKEND_REGISTRY[backendType];
|
||||
const shape: Record<string, z.ZodTypeAny> = {};
|
||||
for (const field of fields) {
|
||||
shape[field.key] = field.required
|
||||
? z.string().min(1, `${field.label} is required`)
|
||||
: z.string().optional();
|
||||
}
|
||||
return z.object(shape);
|
||||
}
|
||||
|
||||
export const BACKEND_SCHEMAS = {
|
||||
azureblob: buildZodSchema('azureblob'),
|
||||
s3: buildZodSchema('s3'),
|
||||
's3-compatible': buildZodSchema('s3-compatible'),
|
||||
} as const;
|
||||
|
||||
// Utility type: infer TypeScript type from a backend's Zod schema
|
||||
export type BackendFormValues<T extends BackendType> =
|
||||
z.infer<typeof BACKEND_SCHEMAS[T]>;
|
||||
```
|
||||
|
||||
Run the tests:
|
||||
```
|
||||
npx vitest run src/schemas/index.test.ts
|
||||
```
|
||||
All tests must pass (GREEN). If any fail, diagnose and fix — do not move on with failing tests.
|
||||
|
||||
Run the full test suite to confirm no regressions:
|
||||
```
|
||||
npx vitest run
|
||||
```
|
||||
</action>
|
||||
<verify>
|
||||
<automated>npx vitest run src/schemas/index.test.ts 2>&1</automated>
|
||||
</verify>
|
||||
<done>All tests in src/schemas/index.test.ts pass. BACKEND_SCHEMAS exported with entries for all three backends. BackendFormValues type exported. npx vitest run (full suite) exits 0 with registry and schema tests green.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
1. `npx vitest run src/schemas/index.test.ts` — all tests pass (12 tests green)
|
||||
2. `npx vitest run src/schemas/registry.test.ts` — still passing (no regression)
|
||||
3. `npx vitest run` — full suite green for all schema tests
|
||||
4. Confirm src/schemas/index.ts has no hardcoded field names in z.object() — only the loop over BACKEND_REGISTRY
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- src/schemas/index.ts creates Zod schemas by looping over BACKEND_REGISTRY field definitions
|
||||
- All 12 tests in src/schemas/index.test.ts pass
|
||||
- BACKEND_SCHEMAS and BackendFormValues are exported
|
||||
- Full test suite (registry + index) exits 0
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/01-foundation/01-03-SUMMARY.md` using the summary template.
|
||||
</output>
|
||||
Reference in New Issue
Block a user