Skip to content

Commit 55b9dde

Browse files
committed
fix: defer policy engine write to disk until flow completes
Previously, pressing Escape on the gateway selection screen during policy engine creation would skip to the success screen because the engine was already written to agentcore.json at the name step. Now the disk write is deferred until the user completes the entire flow, so Escape correctly navigates back to the previous step without persisting a half-configured engine. Constraint: Must not break non-interactive CLI path which still writes immediately via primitive Rejected: Only change Escape to go back without deferring write | engine would still be persisted on back Confidence: high Scope-risk: narrow
1 parent f2964e3 commit 55b9dde

1 file changed

Lines changed: 31 additions & 27 deletions

File tree

src/cli/tui/screens/policy/AddPolicyFlow.tsx

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -126,23 +126,30 @@ export function AddPolicyFlow({ isInteractive = true, onExit, onBack, onDev, onD
126126
}
127127
}, []);
128128

129-
const handleEngineComplete = useCallback(async (config: AddPolicyEngineConfig) => {
130-
const result = await policyEnginePrimitive.add({
131-
name: config.name,
132-
});
129+
const commitEngine = useCallback(async (engineName: string, gateways?: string[], mode?: 'LOG_ONLY' | 'ENFORCE') => {
130+
const result = await policyEnginePrimitive.add({ name: engineName });
131+
if (!result.success) {
132+
setFlow({ name: 'error', message: result.error });
133+
return;
134+
}
135+
setEngineNames(prev => [...prev, engineName]);
136+
if (gateways && gateways.length > 0 && mode) {
137+
await policyEnginePrimitive.attachToGateways(engineName, gateways, mode);
138+
}
139+
setFlow({ name: 'engine-success', engineName });
140+
}, []);
133141

134-
if (result.success) {
135-
setEngineNames(prev => [...prev, config.name]);
142+
const handleEngineComplete = useCallback(
143+
async (config: AddPolicyEngineConfig) => {
136144
const unprotected = await policyEnginePrimitive.getUnprotectedGateways();
137145
if (unprotected.length > 0) {
138146
setFlow({ name: 'attach-gateways', engineName: config.name, gateways: unprotected });
139147
} else {
140-
setFlow({ name: 'engine-success', engineName: config.name });
148+
void commitEngine(config.name);
141149
}
142-
} else {
143-
setFlow({ name: 'error', message: result.error });
144-
}
145-
}, []);
150+
},
151+
[commitEngine]
152+
);
146153

147154
const handlePolicyComplete = useCallback(async (config: AddPolicyConfig) => {
148155
const result = await policyPrimitive.add({
@@ -238,7 +245,7 @@ export function AddPolicyFlow({ isInteractive = true, onExit, onBack, onDev, onD
238245
stepIndicator={<StepIndicator steps={engineSteps} currentStep="attach-gateways" labels={ENGINE_STEP_LABELS} />}
239246
onConfirm={selected => {
240247
if (selected.length === 0) {
241-
setFlow({ name: 'engine-success', engineName: flow.engineName });
248+
void commitEngine(flow.engineName);
242249
} else {
243250
setFlow({
244251
name: 'attach-mode',
@@ -248,7 +255,7 @@ export function AddPolicyFlow({ isInteractive = true, onExit, onBack, onDev, onD
248255
});
249256
}
250257
}}
251-
onSkip={() => setFlow({ name: 'engine-success', engineName: flow.engineName })}
258+
onBack={() => setFlow({ name: 'engine-wizard' })}
252259
/>
253260
);
254261
}
@@ -261,15 +268,12 @@ export function AddPolicyFlow({ isInteractive = true, onExit, onBack, onDev, onD
261268
gatewayCount={flow.selectedGateways.length}
262269
stepIndicator={<StepIndicator steps={engineSteps} currentStep="attach-mode" labels={ENGINE_STEP_LABELS} />}
263270
onSelect={mode => {
264-
void policyEnginePrimitive
265-
.attachToGateways(flow.engineName, flow.selectedGateways, mode)
266-
.then(() => setFlow({ name: 'engine-success', engineName: flow.engineName }))
267-
.catch(err =>
268-
setFlow({
269-
name: 'error',
270-
message: err instanceof Error ? err.message : 'Failed to attach policy engine',
271-
})
272-
);
271+
void commitEngine(flow.engineName, flow.selectedGateways, mode).catch(err =>
272+
setFlow({
273+
name: 'error',
274+
message: err instanceof Error ? err.message : 'Failed to attach policy engine',
275+
})
276+
);
273277
}}
274278
onBack={() => {
275279
setFlow({ name: 'attach-gateways', engineName: flow.engineName, gateways: flow.allGateways });
@@ -360,13 +364,13 @@ function AttachGatewaysScreen({
360364
engineName,
361365
gateways,
362366
onConfirm,
363-
onSkip,
367+
onBack,
364368
stepIndicator,
365369
}: {
366370
engineName: string;
367371
gateways: string[];
368372
onConfirm: (selected: string[]) => void;
369-
onSkip: () => void;
373+
onBack: () => void;
370374
stepIndicator?: React.ReactNode;
371375
}) {
372376
const items: SelectableItem[] = useMemo(() => gateways.map(name => ({ id: name, title: name })), [gateways]);
@@ -375,16 +379,16 @@ function AttachGatewaysScreen({
375379
items,
376380
getId: item => item.id,
377381
onConfirm: ids => onConfirm([...ids]),
378-
onExit: onSkip,
382+
onExit: onBack,
379383
isActive: true,
380384
requireSelection: false,
381385
});
382386

383387
return (
384388
<Screen
385389
title="Attach Policy Engine"
386-
onExit={onSkip}
387-
helpText="Space toggle · Enter confirm · Esc skip · Ctrl+C quit"
390+
onExit={onBack}
391+
helpText="Space toggle · Enter confirm · Esc back · Ctrl+C quit"
388392
headerContent={stepIndicator}
389393
>
390394
<Panel>

0 commit comments

Comments
 (0)