CI surfaced react-hooks/set-state-in-effect on the synchronous setState(computeState(token)) inside the useEffect body. The earlier shape mirrored token -> state via an effect, which is exactly the "you might not need an effect" pattern React 19's eslint rule now flags. Switch to derived state: compute during render, use a useReducer tick to force re-render on the 30s cadence (so relative timestamps stay current even when token props don't change). Same observable behavior, no cascading renders. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
4.1 KiB
4.1 KiB
Parameter Detector — Design Doc
Date: 2026-03-14 Status: Approved Scope: Frontend only (no backend changes)
Overview
A client-side tool in the Script Template Editor that scans a PowerShell script body, detects hardcoded values that should be parameterized, and walks the user through converting them one-by-one via a stepper UI.
Detection Engine
Pure TypeScript utility (lib/scriptParameterDetector.ts) that takes a script body string and returns ParameterCandidate[].
Detection targets (in order)
- Script-level
param()blocks — theparam(...)block at the top of the script, before anyfunctionkeyword. Extracts name, type annotation, and default value. Skipsparam()blocks insidefunctiondeclarations. - Variable assignments —
$VarName = 'value',$VarName = "value",$VarName = 123,$VarName = $true/$false. Skips variables already found in the param block and PowerShell internals like$ErrorActionPreference.
Type inference
| Detection | Suggested Type | Sensitive |
|---|---|---|
[string] or plain string value |
text |
no |
[switch] or $true/$false |
boolean |
no |
[int], [int32], [int64] or numeric value |
number |
no |
[SecureString] or name contains password/secret/key/credential |
password |
yes |
| No type info, string value | text |
no |
Candidate shape
interface ParameterCandidate {
variableName: string // "$OUPath"
suggestedKey: string // "ou_path"
suggestedLabel: string // "OU Path"
suggestedType: ScriptParameter['type']
sensitive: boolean
defaultValue: string | boolean | number | null
source: 'param_block' | 'assignment'
lineNumber: number
matchedLine: string
inferenceReason: string // "Detected [switch] type declaration"
}
Stepper UI
ParameterDetectorStepper component renders inline below the ScriptBodyEditor in the Script Body section.
Trigger
- "Detect Parameters" button (secondary style, Wand2/Scan icon) below the script body textarea
- Hidden if script body is empty; disabled while stepper is active
- If no candidates found: brief "No parameter candidates detected" message
Stepper layout
Shows one candidate at a time with:
- Progress indicator ("Candidate 2 of 5" + dots)
- Matched line displayed in monospace
- Editable fields: Key, Label, Type (with info icon showing inferenceReason), Default value
- Checkboxes: Required, Sensitive
- Actions: Skip, Accept & Next (last item: Accept & Finish / Skip & Finish)
On accept
- Script body: replace the hardcoded value with
{{key}} - Parameters schema: append a new
ScriptParameterwith suggested values + original value asdefault
Edge cases
- Script body edited during detection → stepper dismisses
- Key conflicts with existing parameter → warning + suggested alternative
- Re-running after partial conversion → skips already-converted
{{key}}placeholders
Integration
Component tree
ScriptTemplateEditor
├── Metadata section
├── Script Body section
│ ├── ScriptBodyEditor
│ ├── "Detect Parameters" button ← NEW
│ └── ParameterDetectorStepper ← NEW (conditional)
├── Parameters section
│ └── ParameterSchemaBuilder
└── Fixed Action Bar
Data flow
- Detection runs client-side via
detectParameterCandidates(script_body) - Candidates stored in local React state on ScriptTemplateEditor
- Accept updates
form.script_bodyandform.parameters_schemavia existingupdateField() isDirtyflag set automatically — user can cancel without saving to undo everything- No new backend endpoints needed
File changes
New files:
frontend/src/lib/scriptParameterDetector.tsfrontend/src/components/script-editor/ParameterDetectorStepper.tsx
Modified files:
frontend/src/components/script-editor/ScriptTemplateEditor.tsxfrontend/src/types/scripts.ts
Out of scope
- No backend changes
- No AI-powered detection (future enhancement)
- No auto-detection on paste
- No individual undo for accepted parameters