3030from .handoff import build_runtime_handoff
3131from .kb import bootstrap_kb , ensure_blueprint_index , ensure_blueprint_scaffold
3232from .models import ClarificationState , DecisionState , ExecutionGate , KbArtifact , PlanArtifact , ReplayEvent , RouteDecision , RunState , RuntimeHandoff , RuntimeResult , SkillActivation , SkillMeta
33- from .plan_scaffold import create_plan_scaffold
33+ from .plan_scaffold import (
34+ create_plan_scaffold ,
35+ find_plan_by_request_reference ,
36+ request_explicitly_wants_new_plan ,
37+ )
3438from .replay import ReplayWriter , build_compare_replay_event , build_decision_replay_event
3539from .router import Router
3640from .skill_registry import SkillRegistry
@@ -1197,7 +1201,11 @@ def _advance_planning_route(
11971201 pending_decision = _build_route_native_decision_state (decision , config = config )
11981202 if pending_decision is not None :
11991203 state_store .set_current_decision (pending_decision )
1200- state_store .clear_current_plan ()
1204+ _preserve_or_clear_current_plan_for_pending_decision (
1205+ decision ,
1206+ state_store = state_store ,
1207+ config = config ,
1208+ )
12011209 decision_gate = evaluate_execution_gate (
12021210 decision = decision ,
12031211 plan_artifact = None ,
@@ -1216,20 +1224,21 @@ def _advance_planning_route(
12161224 return (
12171225 _decision_pending_route (decision , reason = "Detected an explicit design split that requires confirmation" ),
12181226 None ,
1219- notes ,
1220- kb_artifact ,
1221- )
1227+ notes ,
1228+ kb_artifact ,
1229+ )
12221230
12231231 level = decision .plan_level or _default_plan_level (decision )
1224- plan_artifact = create_plan_scaffold (
1225- decision .request_text ,
1232+ plan_artifact , plan_notes = _select_plan_for_request (
1233+ decision ,
1234+ state_store = state_store ,
12261235 config = config ,
12271236 level = level ,
1228- decision_state = confirmed_decision ,
1237+ confirmed_decision = confirmed_decision ,
12291238 )
12301239 state_store .set_current_plan (plan_artifact )
12311240 kb_artifact = _merge_kb_artifacts (kb_artifact , ensure_blueprint_index (config ), config = config )
1232- notes .append ( f"Plan scaffold created at { plan_artifact . path } " )
1241+ notes .extend ( plan_notes )
12331242
12341243 routed_decision , plan_artifact , gate_notes = _apply_execution_gate_to_plan (
12351244 decision ,
@@ -1242,6 +1251,73 @@ def _advance_planning_route(
12421251 return (routed_decision , plan_artifact , notes , kb_artifact )
12431252
12441253
1254+ def _select_plan_for_request (
1255+ decision : RouteDecision ,
1256+ * ,
1257+ state_store : StateStore ,
1258+ config : RuntimeConfig ,
1259+ level : str ,
1260+ confirmed_decision : DecisionState | None ,
1261+ ) -> tuple [PlanArtifact , list [str ]]:
1262+ if confirmed_decision is not None :
1263+ created = create_plan_scaffold (
1264+ decision .request_text ,
1265+ config = config ,
1266+ level = level ,
1267+ decision_state = confirmed_decision ,
1268+ )
1269+ return created , [f"Plan scaffold created at { created .path } " ]
1270+
1271+ current_plan = state_store .get_current_plan ()
1272+ explicit_new_plan = request_explicitly_wants_new_plan (decision .request_text )
1273+ explicit_plan = find_plan_by_request_reference (decision .request_text , config = config )
1274+
1275+ if explicit_new_plan :
1276+ created = create_plan_scaffold (
1277+ decision .request_text ,
1278+ config = config ,
1279+ level = level ,
1280+ decision_state = None ,
1281+ )
1282+ return created , [f"Plan scaffold created at { created .path } (explicit new-plan request)" ]
1283+
1284+ if explicit_plan is not None :
1285+ if current_plan is not None and explicit_plan .plan_id == current_plan .plan_id :
1286+ return current_plan , [f"Reused active plan { current_plan .path } (explicit self-reference)" ]
1287+ return explicit_plan , [f"Rebound planning context to existing plan { explicit_plan .path } (explicit plan reference)" ]
1288+
1289+ if current_plan is not None :
1290+ return current_plan , [f"Reused active plan { current_plan .path } under strict single-active-plan policy" ]
1291+
1292+ created = create_plan_scaffold (
1293+ decision .request_text ,
1294+ config = config ,
1295+ level = level ,
1296+ decision_state = None ,
1297+ )
1298+ return created , [f"Plan scaffold created at { created .path } " ]
1299+
1300+
1301+ def _preserve_or_clear_current_plan_for_pending_decision (
1302+ decision : RouteDecision ,
1303+ * ,
1304+ state_store : StateStore ,
1305+ config : RuntimeConfig ,
1306+ ) -> None :
1307+ current_plan = state_store .get_current_plan ()
1308+ if current_plan is None :
1309+ return
1310+
1311+ if request_explicitly_wants_new_plan (decision .request_text ):
1312+ state_store .clear_current_plan ()
1313+ return
1314+
1315+ explicit_plan = find_plan_by_request_reference (decision .request_text , config = config )
1316+ if explicit_plan is not None and explicit_plan .plan_id != current_plan .plan_id :
1317+ state_store .set_current_plan (explicit_plan )
1318+ return
1319+
1320+
12451321def _apply_execution_gate_to_plan (
12461322 decision : RouteDecision ,
12471323 * ,
0 commit comments