4 plans across 3 waves: Wave 0 TDD stubs, Wave 1 data layer (parallel: OneDrive/GCS/B2 + SFTP/SftpAuthToggle), Wave 2 RemoteConfigStep wiring. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
12 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 06-new-backends | 02 | execute | 1 |
|
|
true |
|
|
Purpose: SFTP is the only new backend requiring a custom auth-method toggle (password vs private key). Isolating SFTP work in its own plan keeps Plan 01 clean and allows parallel execution. Output: sftp entry in registry.ts/index.ts/rclone-conf.ts; SftpAuthToggle.tsx component following the AzureAuthToggle CSS-hidden pattern.
<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>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/06-new-backends/06-RESEARCH.md @.planning/phases/06-new-backends/06-00-SUMMARY.mdFrom src/components/wizard/AzureAuthToggle.tsx:
import { useState } from 'react';
import type { UseFormRegister, FieldError } from 'react-hook-form';
import { PasswordField } from '../ui/PasswordField';
type AuthMethod = 'sas' | 'key';
interface AzureAuthToggleProps {
register: UseFormRegister<any>;
errors: { key?: FieldError; sas_url?: FieldError; };
}
export function AzureAuthToggle({ register, errors }: AzureAuthToggleProps) {
const [authMethod, setAuthMethod] = useState<AuthMethod>('sas');
return (
<div className="flex flex-col gap-3">
<div className="flex rounded-md border border-gray-300 overflow-hidden">
<button type="button" onClick={() => setAuthMethod('sas')} className={[
'flex-1 py-1.5 text-sm font-medium transition-colors',
authMethod === 'sas' ? 'bg-blue-600 text-white' : 'bg-white text-gray-700 hover:bg-gray-50',
].join(' ')}>SAS URL</button>
<button type="button" onClick={() => setAuthMethod('key')} className={...}>Access Key</button>
</div>
{/* Both fields always registered — CSS toggling only */}
<div className={authMethod === 'sas' ? 'block' : 'hidden'}>
<PasswordField id="sas_url" label="SAS URL" error={errors.sas_url} registration={register('sas_url')} ... />
</div>
<div className={authMethod === 'key' ? 'block' : 'hidden'}>
<PasswordField id="key" label="Access Key" error={errors.key} registration={register('key')} ... />
</div>
</div>
);
}
From src/schemas/registry.ts (current sftp fields — to add):
// sftp registry entry (add to BACKEND_REGISTRY):
sftp: {
displayName: 'SFTP',
description: 'SSH File Transfer Protocol',
fields: [
{ key: 'host', label: 'Host', inputType: 'text', required: true, placeholder: 'sftp.example.com', helpText: 'The SSH server hostname or IP address' },
{ key: 'user', label: 'Username', inputType: 'text', required: true, placeholder: 'admin' },
{ key: 'pass', label: 'Password', inputType: 'password', required: false, helpText: 'SFTP password. Note: rclone may require the password to be obscured using `rclone obscure <password>`. If authentication fails, use the obscured value.' },
{ key: 'key_pem', label: 'Private Key (PEM)', inputType: 'password', required: false, helpText: 'Paste your private key in PEM format (-----BEGIN ... PRIVATE KEY-----)' },
],
},
```typescript
sftp: {
displayName: 'SFTP',
description: 'SSH File Transfer Protocol',
fields: [
{
key: 'host',
label: 'Host',
inputType: 'text',
required: true,
placeholder: 'sftp.example.com',
helpText: 'The SSH server hostname or IP address',
},
{
key: 'user',
label: 'Username',
inputType: 'text',
required: true,
placeholder: 'admin',
},
{
key: 'pass',
label: 'Password',
inputType: 'password',
required: false,
helpText: 'SFTP password. Note: rclone may require the password to be obscured using `rclone obscure <password>`. If authentication fails, use the obscured value instead of the plain password.',
},
{
key: 'key_pem',
label: 'Private Key (PEM)',
inputType: 'password',
required: false,
helpText: 'Paste your private key in PEM format (-----BEGIN ... PRIVATE KEY-----)',
},
],
},
```
**src/schemas/index.ts:** Add sftp to BACKEND_SCHEMAS:
```typescript
sftp: buildZodSchema('sftp'),
```
**src/generators/rclone-conf.ts:** Add sftp to RCLONE_TYPE_MAP:
```typescript
sftp: 'sftp',
```
Note: BackendType union already includes 'sftp' (added by Plan 01 Task 1). This plan only adds the registry/schema/map entries. If Plan 01 and Plan 02 run in parallel on separate branches, merge Plan 01 first then add sftp entries on top.
Props interface:
```typescript
interface SftpAuthToggleProps {
register: UseFormRegister<any>;
errors: {
pass?: FieldError;
key_pem?: FieldError;
};
}
```
The SFTP toggle test in RemoteConfigStep.test.tsx asserts:
- getByText(/^password$/i, { selector: 'button' }) — button text must be exactly "Password"
- getByText(/private key/i, { selector: 'button' }) — button text must contain "Private Key"
- getByLabelText(/^password$/i) — PasswordField label must be exactly "Password"
- getByLabelText(/private key.*pem/i) — PasswordField label must match "Private Key (PEM)"
Ensure button text and label text match these patterns exactly.
npx tsc --noEmit
Expected: zero errors (sftp now present in BACKEND_REGISTRY satisfying the exhaustive Record type).
<success_criteria>
- sftp registry entry with host, user, pass, key_pem (correct rclone key names)
- SftpAuthToggle.tsx exists, exports SftpAuthToggle, follows AzureAuthToggle CSS-hidden pattern
- sftp in BACKEND_SCHEMAS and RCLONE_TYPE_MAP
- registry and rclone-conf tests fully green for all 7 backends
- Zero TypeScript errors </success_criteria>