docs(05-tech-debt): create phase 5 plan — 4 plans across 2 waves
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>
This commit is contained in:
@@ -0,0 +1,221 @@
|
||||
---
|
||||
phase: 05-tech-debt
|
||||
plan: "02"
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on:
|
||||
- "05-00"
|
||||
files_modified:
|
||||
- src/components/wizard/ReviewStep.tsx
|
||||
autonomous: true
|
||||
requirements:
|
||||
- TECH-01
|
||||
- TECH-02
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "User who deselected RMM sees only the Intune output blocks in ReviewStep (RMM block is unmounted)"
|
||||
- "User who deselected Intune sees only the RMM output block in ReviewStep (Intune blocks are unmounted)"
|
||||
- "User with both targets deselected sees only the rclone.conf block"
|
||||
- "ZIP bundle respects scriptTargets — deselected targets are excluded from the ZIP file list"
|
||||
- "User can click Back on ReviewStep to dispatch SET_STEP(2)"
|
||||
artifacts:
|
||||
- path: "src/components/wizard/ReviewStep.tsx"
|
||||
provides: "scriptTargets-driven conditional rendering + Back button"
|
||||
contains: "showIntune"
|
||||
key_links:
|
||||
- from: "ReviewStep.tsx"
|
||||
to: "state.deployment.scriptTargets"
|
||||
via: "showIntune and showRmm booleans derived from scriptTargets.includes()"
|
||||
pattern: "scriptTargets\\.includes"
|
||||
- from: "Back button"
|
||||
to: "dispatch({ type: 'SET_STEP', payload: 2 })"
|
||||
via: "onClick handler using dispatch from useWizard()"
|
||||
pattern: "SET_STEP.*payload.*2"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Modify ReviewStep to conditionally render output blocks based on state.deployment.scriptTargets, filter the ZIP bundle to match selected targets, and add a Back button that dispatches SET_STEP(2).
|
||||
|
||||
Purpose: TECH-01 eliminates visual noise from deselected script targets. TECH-02 provides the expected Back navigation consistent with the DeploymentStep pattern.
|
||||
Output: Modified ReviewStep.tsx only — no other files touched.
|
||||
</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/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/store/types.ts:
|
||||
```typescript
|
||||
export const INITIAL_STATE: WizardState = {
|
||||
deployment: {
|
||||
scriptTargets: ['intune', 'rmm'], // default = both selected
|
||||
},
|
||||
};
|
||||
export type WizardAction =
|
||||
| { type: 'SET_STEP'; payload: number }
|
||||
| { type: 'SET_DEPLOYMENT'; payload: Partial<WizardState['deployment']> }
|
||||
// ...
|
||||
```
|
||||
|
||||
From src/store/context.tsx:
|
||||
```typescript
|
||||
export function useWizard(): { state: WizardState; dispatch: React.Dispatch<WizardAction> }
|
||||
```
|
||||
|
||||
Current ReviewStep.tsx signature:
|
||||
```typescript
|
||||
const { state } = useWizard(); // dispatch is NOT currently destructured — must add it
|
||||
```
|
||||
|
||||
From CONTEXT.md — locked decisions:
|
||||
- Intune maps to: intuneInstall + intuneDetection blocks
|
||||
- RMM maps to: rmmScript block
|
||||
- rclone.conf OutputBlock is ALWAYS shown regardless of scriptTargets
|
||||
- Edge case: both deselected → only rclone.conf shown + ZIP has only rclone.conf
|
||||
- Unmount entirely (NOT CSS-hidden) — no state to preserve between show/hide
|
||||
|
||||
Back button pattern (from DeploymentStep.tsx):
|
||||
```tsx
|
||||
<div className="flex gap-3 mt-6">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => dispatch({ type: 'SET_STEP', payload: 2 })}
|
||||
className="px-4 py-2 text-sm border border-gray-300 rounded-md hover:bg-gray-50"
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
Note: payload is 2 because DeploymentStep is step index 2.
|
||||
</interfaces>
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 1: Add scriptTargets filtering and Back button to ReviewStep</name>
|
||||
<files>src/components/wizard/ReviewStep.tsx</files>
|
||||
<behavior>
|
||||
- showIntune = state.deployment.scriptTargets.includes('intune') — boolean
|
||||
- showRmm = state.deployment.scriptTargets.includes('rmm') — boolean
|
||||
- {showIntune && <OutputBlock label="Intune Install Script" .../>} — unmounted when false
|
||||
- {showIntune && <OutputBlock label="Intune Detection Script" .../>} — unmounted when false
|
||||
- {showRmm && <OutputBlock label="RMM Script" .../>} — unmounted when false
|
||||
- rclone.conf OutputBlock always rendered (no condition)
|
||||
- handleDownloadZip builds files array dynamically: rclone.conf always, intune files if showIntune, rmm file if showRmm
|
||||
- Back button renders at bottom (before ZIP button) with label 'Back', dispatches SET_STEP(2) on click
|
||||
- useMemo calls for intuneInstall, intuneDetection, rmmScript are KEPT (used in ZIP handler even when hidden)
|
||||
</behavior>
|
||||
<action>
|
||||
Edit src/components/wizard/ReviewStep.tsx:
|
||||
|
||||
1. Change `const { state } = useWizard();` to `const { state, dispatch } = useWizard();`
|
||||
|
||||
2. After the useMemo calls, add two boolean derivations:
|
||||
```tsx
|
||||
const showIntune = state.deployment.scriptTargets.includes('intune');
|
||||
const showRmm = state.deployment.scriptTargets.includes('rmm');
|
||||
```
|
||||
|
||||
3. Replace handleDownloadZip with the dynamic version:
|
||||
```tsx
|
||||
async function handleDownloadZip() {
|
||||
const files: { name: string; content: string }[] = [
|
||||
{ name: 'rclone.conf', content: rcloneConf },
|
||||
];
|
||||
if (showIntune) {
|
||||
files.push({ name: 'intune-install.ps1', content: intuneInstall });
|
||||
files.push({ name: 'intune-detection.ps1', content: intuneDetection });
|
||||
}
|
||||
if (showRmm) {
|
||||
files.push({ name: 'rmm-script.ps1', content: rmmScript });
|
||||
}
|
||||
await downloadZip(files, 'rclone-deployment.zip');
|
||||
}
|
||||
```
|
||||
|
||||
4. Wrap Intune OutputBlocks with showIntune condition (unmount entirely):
|
||||
```tsx
|
||||
{showIntune && (
|
||||
<OutputBlock label="Intune Install Script" content={intuneInstall} filename="intune-install.ps1" disabled={!acknowledged} />
|
||||
)}
|
||||
{showIntune && (
|
||||
<OutputBlock label="Intune Detection Script" content={intuneDetection} filename="intune-detection.ps1" disabled={!acknowledged} />
|
||||
)}
|
||||
```
|
||||
|
||||
5. Wrap RMM OutputBlock with showRmm condition:
|
||||
```tsx
|
||||
{showRmm && (
|
||||
<OutputBlock label="RMM Script" content={rmmScript} filename="rmm-script.ps1" disabled={!acknowledged} />
|
||||
)}
|
||||
```
|
||||
|
||||
6. Add Back button before the ZIP button, following DeploymentStep's established pattern:
|
||||
```tsx
|
||||
<div className="flex gap-3 mt-6">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => dispatch({ type: 'SET_STEP', payload: 2 })}
|
||||
className="px-4 py-2 text-sm border border-gray-300 rounded-md hover:bg-gray-50"
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
Do NOT remove any existing comment lines (SECU-01, SECU-02, etc.).
|
||||
Do NOT touch any useMemo calls — they are needed for ZIP handler even when the blocks are hidden.
|
||||
The existing DOWN-05 test (both targets selected = 4 files in ZIP) still passes because INITIAL_STATE has scriptTargets=['intune','rmm'].
|
||||
The existing DOWN-02, DOWN-03, DOWN-04 tests still pass because they use default state with both targets.
|
||||
|
||||
Anti-patterns to avoid:
|
||||
- Do NOT use CSS `hidden` class — must unmount (conditional render with &&)
|
||||
- Do NOT wrap rclone.conf OutputBlock in a condition — it is always shown
|
||||
</action>
|
||||
<verify>
|
||||
<automated>cd /c/Users/SebastienQUEROL/Documents/projets/Ready2Blob && npx vitest run src/components/wizard/ReviewStep.test.tsx 2>&1 | tail -20</automated>
|
||||
</verify>
|
||||
<done>
|
||||
All ReviewStep tests pass (green), including:
|
||||
- Pre-existing CONF-02, CONF-03, DOWN-01 through DOWN-06, SECU-01, SECU-02
|
||||
- New TECH-01 filtering cases (intune-only, rmm-only, neither, ZIP-neither)
|
||||
- New TECH-02 Back button case
|
||||
Zero act() warnings in output (existing tests use fireEvent on checkbox/download buttons which do not trigger async state updates outside the component).
|
||||
</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
Run full suite to confirm ReviewStep is green and no regressions:
|
||||
```
|
||||
cd /c/Users/SebastienQUEROL/Documents/projets/Ready2Blob && npx vitest run
|
||||
```
|
||||
Expected: All ReviewStep tests green. TECH-01 and TECH-02 stubs from Plan 00 now pass.
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- ReviewStep conditionally renders Intune blocks based on showIntune boolean (conditional render, not CSS)
|
||||
- ReviewStep conditionally renders RMM block based on showRmm boolean
|
||||
- rclone.conf OutputBlock always renders
|
||||
- handleDownloadZip builds files array filtered by showIntune/showRmm
|
||||
- Back button renders and dispatches SET_STEP(2) via dispatch
|
||||
- All pre-existing ReviewStep tests still pass (DOWN-02/03/04 positional index tests unaffected — default state has both targets)
|
||||
- New TECH-01 and TECH-02 tests from Plan 00 now pass (GREEN state)
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/05-tech-debt/05-02-SUMMARY.md`
|
||||
</output>
|
||||
Reference in New Issue
Block a user