docs(02-generators): create phase 2 plan — 4 plans in 2 waves
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
---
|
||||
phase: 02-generators
|
||||
plan: 02
|
||||
type: execute
|
||||
wave: 2
|
||||
depends_on:
|
||||
- 02-01
|
||||
files_modified:
|
||||
- src/generators/rclone-conf.ts
|
||||
autonomous: true
|
||||
requirements:
|
||||
- CONF-01
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "buildRcloneConf(azureState) produces a valid INI block with type = azureblob and non-empty fields"
|
||||
- "buildRcloneConf(s3State) produces type = s3 with provider = AWS"
|
||||
- "buildRcloneConf(s3CompatState) produces type = s3 (not s3-compatible) with provider = Other"
|
||||
- "Empty optional fields are omitted from the output (no 'sas_url =' line when sas_url is empty)"
|
||||
- "All rclone-conf.test.ts assertions pass GREEN"
|
||||
artifacts:
|
||||
- path: "src/generators/rclone-conf.ts"
|
||||
provides: "buildRcloneConf pure function"
|
||||
exports: ["buildRcloneConf"]
|
||||
min_lines: 25
|
||||
key_links:
|
||||
- from: "src/generators/rclone-conf.ts"
|
||||
to: "src/store/types.ts"
|
||||
via: "import type { WizardState } from '../store/types'"
|
||||
pattern: "WizardState"
|
||||
- from: "RCLONE_TYPE_MAP"
|
||||
to: "rclone backend type strings"
|
||||
via: "Record<string, string> lookup"
|
||||
pattern: "'s3-compatible': 's3'"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Implement buildRcloneConf() — the pure TypeScript function that converts WizardState into a valid rclone.conf INI string.
|
||||
|
||||
Purpose: CONF-01 — the foundational output artifact. Every downstream phase depends on this being correct. Wrong type values or included empty fields silently break rclone remotes.
|
||||
Output: src/generators/rclone-conf.ts with all rclone-conf.test.ts assertions green.
|
||||
</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/ROADMAP.md
|
||||
@.planning/phases/02-generators/02-01-SUMMARY.md
|
||||
@src/store/types.ts
|
||||
@src/schemas/registry.ts
|
||||
@src/generators/rclone-conf.test.ts
|
||||
</context>
|
||||
|
||||
<interfaces>
|
||||
<!-- Types consumed by buildRcloneConf. -->
|
||||
|
||||
From src/store/types.ts:
|
||||
```typescript
|
||||
export interface WizardState {
|
||||
currentStep: number;
|
||||
remote: {
|
||||
name: string;
|
||||
backendType: BackendType | null; // null must throw
|
||||
params: Record<string, string>; // iterate, skip empty values
|
||||
};
|
||||
deployment: {
|
||||
includeInstall: boolean;
|
||||
configPath: 'machine-wide' | 'user-profile';
|
||||
scriptTargets: ('intune' | 'rmm')[];
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
From src/schemas/registry.ts:
|
||||
```typescript
|
||||
export type BackendType = 'azureblob' | 's3' | 's3-compatible';
|
||||
```
|
||||
|
||||
Critical type mapping (from research — verified against rclone.org):
|
||||
```typescript
|
||||
// BackendType → rclone config 'type' value
|
||||
// 'azureblob' → 'azureblob'
|
||||
// 's3' → 's3'
|
||||
// 's3-compatible' → 's3' (NOT 's3-compatible' — rclone does not recognize that string)
|
||||
// S3-compatible backends use type = s3, differentiated by provider = Other
|
||||
```
|
||||
</interfaces>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 1: Implement buildRcloneConf</name>
|
||||
<files>src/generators/rclone-conf.ts</files>
|
||||
<behavior>
|
||||
- azureblob: '[my-azure]\ntype = azureblob\naccount = mystorageaccount\nkey = BASE64KEY\n' (sas_url omitted — empty)
|
||||
- s3: contains 'type = s3', 'provider = AWS', 'access_key_id = AKID', 'region = us-east-1'
|
||||
- s3-compatible: contains 'type = s3', NOT 'type = s3-compatible', 'provider = Other', 'endpoint = https://s3.wasabisys.com', NOT 'region' line (empty optional field)
|
||||
- throws Error if backendType is null
|
||||
- throws Error if name is empty string
|
||||
- output ends with '\n' (trailing newline — rclone convention)
|
||||
- no empty value lines: `if (value !== '') { lines.push(...) }`
|
||||
</behavior>
|
||||
<action>
|
||||
Create src/generators/rclone-conf.ts implementing buildRcloneConf(state: WizardState): string.
|
||||
|
||||
Follow this exact structure:
|
||||
```typescript
|
||||
import type { WizardState } from '../store/types';
|
||||
|
||||
// Maps our BackendType to rclone's internal type string.
|
||||
// IMPORTANT: s3-compatible uses 'type = s3' — rclone does not recognize 's3-compatible' as a type.
|
||||
// S3-compatible backends are differentiated by provider = Other in params.
|
||||
const RCLONE_TYPE_MAP: Record<string, string> = {
|
||||
azureblob: 'azureblob',
|
||||
s3: 's3',
|
||||
's3-compatible': 's3',
|
||||
};
|
||||
|
||||
export function buildRcloneConf(state: WizardState): string {
|
||||
const { name, backendType, params } = state.remote;
|
||||
if (!backendType) throw new Error('buildRcloneConf: backendType is required');
|
||||
if (!name) throw new Error('buildRcloneConf: remote name is required');
|
||||
|
||||
const rcloneType = RCLONE_TYPE_MAP[backendType];
|
||||
const lines: string[] = [`[${name}]`, `type = ${rcloneType}`];
|
||||
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
if (value !== '') {
|
||||
lines.push(`${key} = ${value}`);
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n') + '\n';
|
||||
}
|
||||
```
|
||||
|
||||
Do NOT import BACKEND_REGISTRY — the function iterates over state.remote.params directly (already contains the right keys from the registry-driven form). Do NOT add any console.log or Write-Host equivalent — credentials must not be logged.
|
||||
|
||||
Anti-pattern to avoid: Do not write `type = s3-compatible`. The RCLONE_TYPE_MAP lookup handles the translation.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>npm test -- src/generators/rclone-conf.test.ts</automated>
|
||||
</verify>
|
||||
<done>
|
||||
All rclone-conf.test.ts assertions pass GREEN. buildRcloneConf produces correct INI output for all three backends. Empty fields are omitted. Type mapping is correct (s3-compatible → type = s3).
|
||||
</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
npm test -- src/generators/rclone-conf.test.ts is fully green.
|
||||
npm test (full suite) still passes.
|
||||
src/generators/rclone-conf.ts is importable from index.ts without TypeScript errors.
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
CONF-01 is satisfied: buildRcloneConf produces valid INI-format rclone.conf strings with correct key/value pairs for all three supported backends (azureblob, s3, s3-compatible).
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/02-generators/02-02-SUMMARY.md`
|
||||
</output>
|
||||
Reference in New Issue
Block a user