docs(07): create phase plan — 3 plans for VALID-01 and UX-01

This commit is contained in:
2026-03-31 09:20:11 +02:00
parent 78f54e3c9e
commit 9d02764fa3
4 changed files with 672 additions and 2 deletions
@@ -0,0 +1,209 @@
---
phase: 07-validation-ux-polish
plan: "01"
type: execute
wave: 2
depends_on:
- "07-00"
files_modified:
- src/schemas/registry.ts
- src/schemas/index.ts
autonomous: true
requirements:
- VALID-01
must_haves:
truths:
- "User who enters 'MyStorage' in the Azure account name field sees an inline error before advancing"
- "User who enters 'us east 1' in the S3 region field sees an inline error"
- "User who enters 'abc' in the GCS project_number field sees an inline error"
- "Valid values ('mystorageaccount', 'us-east-1', '123456789') produce no format error"
- "Empty required fields still show 'required' error (not the regex error) — empty string hits min(1) before regex"
- "All 147 pre-existing tests still pass after adding validation"
artifacts:
- path: "src/schemas/registry.ts"
provides: "FieldDef interface with validate property; 3 registry entries with validate rules"
contains: "validate\\?: \\{ regex: RegExp; message: string \\}"
- path: "src/schemas/index.ts"
provides: "buildZodSchema() chains .regex() when field.validate is present"
contains: "field\\.validate"
key_links:
- from: "BACKEND_REGISTRY azureblob.account"
to: "buildZodSchema('azureblob')"
via: "field.validate.regex applied as .regex() on ZodString"
pattern: "field\\.validate"
- from: "buildZodSchema()"
to: "BACKEND_SCHEMAS"
via: "Zod schema exported, consumed by zodResolver in RemoteConfigStep"
---
<objective>
Implement VALID-01: extend FieldDef with a validate property, add regex rules to 3 registry entries (azureblob account, s3 region, gcs project_number), and extend buildZodSchema() to chain .regex() when the property is present.
Purpose: Users see inline format errors on malformed credential fields before they can advance in the wizard.
Output: Modified registry.ts (FieldDef + 3 validate rules) and index.ts (buildZodSchema regex chaining).
</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/07-validation-ux-polish/07-CONTEXT.md
@.planning/phases/07-validation-ux-polish/07-RESEARCH.md
@.planning/phases/07-validation-ux-polish/07-00-SUMMARY.md
<interfaces>
<!-- Extracted from codebase. Executor uses these directly. -->
Current FieldDef (src/schemas/registry.ts):
```typescript
export interface FieldDef {
key: string;
label: string;
inputType: 'text' | 'password' | 'select' | 'toggle';
required: boolean;
placeholder?: string;
helpText?: string;
options?: { value: string; label: string }[];
// validate and tooltipText DO NOT EXIST YET
}
```
Current buildZodSchema (src/schemas/index.ts):
```typescript
function buildZodSchema(backendType: BackendType): z.ZodObject<Record<string, z.ZodTypeAny>> {
const fields = BACKEND_REGISTRY[backendType].fields;
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);
}
```
Zod v4 important: cast to (schema as z.ZodString).regex(...) because schema variable is typed ZodTypeAny.
Zod v4 chaining order: .min(1).regex() is correct for required fields. .regex().optional() for optional (not needed here — all 3 validated fields are required).
</interfaces>
</context>
<tasks>
<task type="auto" tdd="true">
<name>Task 1: Extend FieldDef and add validate rules to 3 registry entries</name>
<files>src/schemas/registry.ts</files>
<behavior>
- Adding validate to azureblob.account and submitting 'ABC' produces Zod error 'Must be 324 lowercase alphanumeric characters'
- Adding validate to s3.region and submitting 'us east 1' produces Zod error matching region format
- Adding validate to gcs.project_number and submitting 'abc' produces Zod error 'digits only' (or similar)
- Submitting '' on account still produces the required error (not the regex error) — min(1) fires first
</behavior>
<action>
1. Add two optional properties to FieldDef interface:
```typescript
validate?: { regex: RegExp; message: string };
tooltipText?: string; // also add here for Plan 02 — it's just an interface addition, no behavior yet
```
Note: Adding tooltipText here avoids a second interface-only edit in Plan 02.
2. In BACKEND_REGISTRY, locate the azureblob `account` field entry and add:
```typescript
validate: {
regex: /^[a-z0-9]{3,24}$/,
message: 'Must be 324 lowercase alphanumeric characters (no hyphens or uppercase)',
},
```
3. Locate the s3 `region` field entry and add:
```typescript
validate: {
regex: /^[a-z][a-z0-9-]+[a-z0-9]$/,
message: 'Must be a valid AWS region format (e.g. us-east-1)',
},
```
4. Locate the gcs `project_number` field entry and add:
```typescript
validate: {
regex: /^\d+$/,
message: 'Must contain digits only',
},
```
No other fields get validate rules — user decision is explicit on scope.
</action>
<verify>
<automated>npx vitest run src/components/wizard/RemoteConfigStep.test.tsx</automated>
</verify>
<done>FieldDef has validate and tooltipText properties. 3 registry entries have validate rules. TypeScript compiles without errors (npx tsc --noEmit).</done>
</task>
<task type="auto" tdd="true">
<name>Task 2: Extend buildZodSchema() to chain .regex() from field.validate</name>
<files>src/schemas/index.ts</files>
<behavior>
- buildZodSchema('azureblob') produces a schema where the account field rejects 'ABC' with the registry message
- buildZodSchema('s3') produces a schema where the region field rejects 'us east 1'
- buildZodSchema('gcs') produces a schema where project_number rejects 'abc'
- buildZodSchema('onedrive') and others without validate are unaffected
- VALID-01 failing tests from Plan 00 now PASS
</behavior>
<action>
Replace the buildZodSchema loop body with the regex-aware version:
```typescript
function buildZodSchema(backendType: BackendType): z.ZodObject<Record<string, z.ZodTypeAny>> {
const fields = BACKEND_REGISTRY[backendType].fields;
const shape: Record<string, z.ZodTypeAny> = {};
for (const field of fields) {
let schema: z.ZodTypeAny = field.required
? z.string().min(1, `${field.label} is required`)
: z.string();
if (field.validate) {
schema = (schema as z.ZodString).regex(field.validate.regex, field.validate.message);
}
if (!field.required) {
schema = (schema as z.ZodString).optional();
}
shape[field.key] = schema;
}
return z.object(shape);
}
```
Critical: cast to (schema as z.ZodString) before .regex() — ZodTypeAny does not expose .regex() in TypeScript but it is present at runtime. This pattern is verified.
Do NOT touch BACKEND_SCHEMAS export or anything else in the file.
</action>
<verify>
<automated>npx vitest run src/components/wizard/RemoteConfigStep.test.tsx</automated>
</verify>
<done>VALID-01 tests from Plan 00 all pass (GREEN). Full suite still at 147+ pass with 0 failures: `npx vitest run`.</done>
</task>
</tasks>
<verification>
1. `npx tsc --noEmit` — no TypeScript errors
2. `npx vitest run src/components/wizard/RemoteConfigStep.test.tsx` — VALID-01 tests pass (GREEN), UX-01 stubs still fail (expected — UX-01 is Plan 02)
3. `npx vitest run` — all pre-existing 147 tests pass, no regressions
</verification>
<success_criteria>
- FieldDef has validate and tooltipText properties
- 3 registry entries (azureblob.account, s3.region, gcs.project_number) have validate rules with the exact regexes from CONTEXT.md
- buildZodSchema() chains .regex() when field.validate is present
- VALID-01 test stubs from Plan 00 all pass
- Full test suite passes
</success_criteria>
<output>
After completion, create `.planning/phases/07-validation-ux-polish/07-01-SUMMARY.md`
</output>