mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-11 05:11:49 +01:00
fix(runner): refuse to reopen a session whose run failed its turn-end restart
open_session did not check the saved turn-end restart failure, so session.open or a same-run run.attach reopened the provider and later turns ran. Refuse there while the marker names the current run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f5d5d35d90
commit
7fe9345030
2 files changed
+31
-1
No files matched your search
@@ -1788,6 +1788,17 @@ impl AcpxCommandExecutor {
|
|||||||
"ACPX provider lifetime cleanup is not yet proven",
|
"ACPX provider lifetime cleanup is not yet proven",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
// No path (session.open, a same-run run.attach) reopens a session whose
|
||||||
|
// run failed closed at its turn-end provider restart.
|
||||||
|
if self.session.is_none()
|
||||||
|
&& self.state.as_ref().is_some_and(|state| {
|
||||||
|
state.turn_end_restart_failed_run.as_deref() == Some(self.context.run_id.as_str())
|
||||||
|
})
|
||||||
|
{
|
||||||
|
return Err(DurableRunnerError::invalid(
|
||||||
|
"ACPX session failed closed after this run's turn-end provider restart failed",
|
||||||
|
));
|
||||||
|
}
|
||||||
if self.session.is_none() {
|
if self.session.is_none() {
|
||||||
let recovering = self
|
let recovering = self
|
||||||
.state
|
.state
|
||||||
|
|||||||
+20
-1
@@ -441,11 +441,30 @@ fn a_runner_restart_in_the_same_run_keeps_the_failed_turn_end_restart_closed() {
|
|||||||
.to_string();
|
.to_string();
|
||||||
assert_eq!(failed, "ACPX session is not open");
|
assert_eq!(failed, "ACPX session is not open");
|
||||||
assert_eq!(harness.fixture.process_starts().len(), starts);
|
assert_eq!(harness.fixture.process_starts().len(), starts);
|
||||||
|
// Neither session.open nor a same-run attach reopens it.
|
||||||
|
let reopened = harness
|
||||||
|
.executor
|
||||||
|
.execute(&command(91, "session.open", json!({})))
|
||||||
|
.unwrap_err()
|
||||||
|
.to_string();
|
||||||
|
assert!(reopened.contains("failed closed"), "{reopened}");
|
||||||
|
let same_run_attach = json!({"provider": harness.descriptor.clone()});
|
||||||
|
let attach_error = harness
|
||||||
|
.executor
|
||||||
|
.execute(&command(92, "run.attach", same_run_attach))
|
||||||
|
.unwrap_err()
|
||||||
|
.to_string();
|
||||||
|
assert!(attach_error.contains("failed closed"), "{attach_error}");
|
||||||
|
assert_eq!(harness.fixture.process_starts().len(), starts);
|
||||||
|
assert_eq!(
|
||||||
|
harness.fixture.state()["turnEndRestartFailedRun"],
|
||||||
|
harness.config.run_id
|
||||||
|
);
|
||||||
assert_eq!(live_providers(&harness), Vec::<Value>::new());
|
assert_eq!(live_providers(&harness), Vec::<Value>::new());
|
||||||
assert_eq!(harness.fixture.state()["lifecycle"], "suspended");
|
assert_eq!(harness.fixture.state()["lifecycle"], "suspended");
|
||||||
// The next run still resumes the conversation and clears the restriction.
|
// The next run still resumes the conversation and clears the restriction.
|
||||||
harness.executor.shutdown().unwrap();
|
harness.executor.shutdown().unwrap();
|
||||||
harness.sequence = 90;
|
harness.sequence = 92;
|
||||||
harness.config.run_id = "run-2".to_owned();
|
harness.config.run_id = "run-2".to_owned();
|
||||||
harness.executor =
|
harness.executor =
|
||||||
NativeProviderCommandExecutor::with_runner_config(&harness.fixture.0, &harness.config);
|
NativeProviderCommandExecutor::with_runner_config(&harness.fixture.0, &harness.config);
|
||||||
|
|||||||
Reference in new issue
Block a user