删除已退役 Supervisor 的持久进程协作治理用例
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
- tests/command_runtime.rs:删除 `process_session_supervisor_collaboration_blocks_stdin_but_allows_poll_and_terminate_in_isolated_registry`——Supervisor 已于 2026-09-21 永久退役(项目对话收敛为 DirectProject 与 Design Agent,前端零引用),这条用例不再有现役调用方 - tests/mod.rs:同批删除它唯一的夹具 `process_session_supervisor_collaboration_governance_fixture`(256 行) - 保留 `process_session_agent_runtime_*_isolated_registry` 两条兄弟用例:它们走的是未退役的 `code-prototype` 路径,且仍是通用 process_session 收口的守门人 - 该用例也是分片偶发假红的来源:慢容器上 terminate 收口按设计翻成 needs-reconciliation(finalize_live_process_session 的 2 秒排水窗口),退役面不值得继续钉 - 验证:用例总数 3154 → 3153;两条兄弟用例通过;无新增编译告警;check:encoding、git diff --check 通过
This commit is contained in:
@@ -4109,34 +4109,6 @@ fn process_session_agent_runtime_confirmed_lifecycle_runs_in_isolated_registry()
|
||||
assert!(status.success(), "isolated lifecycle test failed: {status}");
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn process_session_supervisor_collaboration_blocks_stdin_but_allows_poll_and_terminate_in_isolated_registry(
|
||||
) {
|
||||
const CHILD_MARKER: &str =
|
||||
"GENARRATIVE_PROCESS_SESSION_SUPERVISOR_COLLABORATION_GOVERNANCE_CHILD";
|
||||
if std::env::var_os(CHILD_MARKER).is_some() {
|
||||
tauri::async_runtime::block_on(
|
||||
process_session_supervisor_collaboration_governance_fixture(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
let status = std::process::Command::new(std::env::current_exe().expect("current test binary"))
|
||||
.env(CHILD_MARKER, "1")
|
||||
.args([
|
||||
"--exact",
|
||||
"tests::command_runtime::process_session_supervisor_collaboration_blocks_stdin_but_allows_poll_and_terminate_in_isolated_registry",
|
||||
"--nocapture",
|
||||
"--test-threads=1",
|
||||
])
|
||||
.status()
|
||||
.expect("spawn isolated Supervisor process collaboration governance test");
|
||||
assert!(
|
||||
status.success(),
|
||||
"isolated Supervisor process collaboration governance test failed: {status}"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
fn process_session_agent_runtime_start_audit_failure_terminates_target_in_isolated_registry() {
|
||||
|
||||
@@ -4796,262 +4796,6 @@ async fn execute_approved_process_action_for_test(
|
||||
(pending, observation)
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
async fn process_session_supervisor_collaboration_governance_fixture() {
|
||||
const READY_SENTINEL: &str = "SUPERVISOR_PROCESS_READY_PRIVATE";
|
||||
const POLL_SENTINEL: &str = "SUPERVISOR_PROCESS_POLL_PRIVATE";
|
||||
const BLOCKED_STDIN_SENTINEL: &str = "SUPERVISOR_PROCESS_BLOCKED_STDIN_PRIVATE";
|
||||
const AGENT_ID: &str = GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID;
|
||||
const RUN_ID: &str = "supervisor-process-collaboration-governance-run";
|
||||
clear_process_session_registry_for_tests();
|
||||
let root = unique_project_path();
|
||||
init_local_game_project_at(
|
||||
&root,
|
||||
"supervisor-process-collaboration-governance-project",
|
||||
"Project Supervisor 持久进程治理项目",
|
||||
)
|
||||
.expect("project init");
|
||||
let _cleanup = ProcessSessionIntegrationCleanup {
|
||||
root: root.clone(),
|
||||
agent_id: AGENT_ID,
|
||||
run_id: RUN_ID,
|
||||
};
|
||||
fs::write(
|
||||
root.join("package.json"),
|
||||
r#"{"private":true,"scripts":{"dev":"node supervisor-process-fixture.mjs"}}"#,
|
||||
)
|
||||
.expect("write Supervisor process package");
|
||||
fs::write(
|
||||
root.join("supervisor-process-fixture.mjs"),
|
||||
format!(
|
||||
r#"process.stdin.setEncoding('utf8');
|
||||
console.log('{READY_SENTINEL}');
|
||||
process.stdin.on('data', (chunk) => console.log(`ECHO:${{chunk.trim()}}`));
|
||||
setInterval(() => console.log('{POLL_SENTINEL}'), 50);
|
||||
"#
|
||||
),
|
||||
)
|
||||
.expect("write Supervisor process fixture");
|
||||
write_supervisor_collaboration_policy_at(&root, SupervisorCollaborationPolicy::default())
|
||||
.expect("write Supervisor collaboration policy");
|
||||
write_project_permission_policy_at(
|
||||
&root,
|
||||
ProjectPermissionPolicy {
|
||||
denied_commands: Vec::new(),
|
||||
confirm_commands: vec![
|
||||
"command.start".to_string(),
|
||||
"command.stdin".to_string(),
|
||||
"command.terminate".to_string(),
|
||||
],
|
||||
agent_policies: BTreeMap::new(),
|
||||
},
|
||||
)
|
||||
.expect("write process tool policy");
|
||||
let mut state = start_game_creator_agent_runtime_task_at(
|
||||
&root,
|
||||
AGENT_ID,
|
||||
"在协作 delivery 前启动持久进程并验证后续治理边界",
|
||||
RUN_ID,
|
||||
"agent-chat",
|
||||
"启动并治理持久进程",
|
||||
vec!["进程可读取且可终止,协作后 stdin 被阻断".to_string()],
|
||||
)
|
||||
.expect("start Supervisor process runtime");
|
||||
state.loop_iteration = 1;
|
||||
|
||||
let (_, start_observation) = execute_approved_process_action_for_test(
|
||||
&root,
|
||||
&mut state,
|
||||
AgentRuntimeToolAction {
|
||||
tool: "command.start".to_string(),
|
||||
reason: Some("在 delivery 前启动真实持久进程".to_string()),
|
||||
input: serde_json::json!({
|
||||
"program": "npm",
|
||||
"args": ["run", "dev"],
|
||||
"cwd": ".",
|
||||
"timeoutSeconds": 30
|
||||
}),
|
||||
},
|
||||
AGENT_RUNTIME_ACTION_EXECUTION_MODE_CONFIRMATION,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(start_observation.status, "ok", "{start_observation:?}");
|
||||
let start_detail: Value = serde_json::from_str(
|
||||
start_observation
|
||||
.detail
|
||||
.as_deref()
|
||||
.expect("start observation detail"),
|
||||
)
|
||||
.expect("parse start observation detail");
|
||||
let process_id = start_detail["processId"]
|
||||
.as_str()
|
||||
.expect("start processId")
|
||||
.to_string();
|
||||
let mut cursor = start_detail["nextCursor"]
|
||||
.as_str()
|
||||
.expect("start next cursor")
|
||||
.to_string();
|
||||
|
||||
let mut ready_output = String::new();
|
||||
for index in 0..20 {
|
||||
let (_, observation) = execute_approved_process_action_for_test(
|
||||
&root,
|
||||
&mut state,
|
||||
AgentRuntimeToolAction {
|
||||
tool: "command.poll".to_string(),
|
||||
reason: Some(format!("等待 Supervisor fixture ready {index}")),
|
||||
input: serde_json::json!({
|
||||
"processId": process_id,
|
||||
"cursor": cursor,
|
||||
"maxChars": 8_000,
|
||||
"waitMs": 500
|
||||
}),
|
||||
},
|
||||
AGENT_RUNTIME_ACTION_EXECUTION_MODE_AUTO,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(observation.status, "ok", "{observation:?}");
|
||||
let detail: Value = serde_json::from_str(
|
||||
observation
|
||||
.detail
|
||||
.as_deref()
|
||||
.expect("ready poll observation detail"),
|
||||
)
|
||||
.expect("parse ready poll detail");
|
||||
assert_eq!(detail["processId"], process_id);
|
||||
ready_output.push_str(detail["output"].as_str().unwrap_or_default());
|
||||
cursor = detail["nextCursor"]
|
||||
.as_str()
|
||||
.expect("ready poll next cursor")
|
||||
.to_string();
|
||||
if ready_output.contains(READY_SENTINEL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert!(ready_output.contains(READY_SENTINEL), "{ready_output}");
|
||||
|
||||
let delivery = new_static_delegate_delivery(
|
||||
AGENT_ID,
|
||||
&state.session_id,
|
||||
RUN_ID,
|
||||
"supervisor-process-collaboration-delegate-action",
|
||||
"supervisor-process-collaboration-delivery",
|
||||
"code-prototype",
|
||||
"supervisor-process-collaboration-child-session",
|
||||
"supervisor-process-collaboration-child-run",
|
||||
);
|
||||
create_or_read_static_delegate_delivery_at(&root, &delivery)
|
||||
.expect("create same-run durable static delivery");
|
||||
|
||||
let blocked_stdin = execute_game_creator_agent_runtime_tool_action(
|
||||
&root,
|
||||
AGENT_ID,
|
||||
RUN_ID,
|
||||
&state.current_task,
|
||||
&AgentRuntimeToolAction {
|
||||
tool: "command.stdin".to_string(),
|
||||
reason: Some("协作后不得继续写入持久进程".to_string()),
|
||||
input: serde_json::json!({
|
||||
"processId": process_id,
|
||||
"data": BLOCKED_STDIN_SENTINEL,
|
||||
"appendNewline": true,
|
||||
"eof": false
|
||||
}),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
assert_eq!(blocked_stdin.status, "blocked", "{blocked_stdin:?}");
|
||||
assert!(blocked_stdin.summary.contains("协作编排"));
|
||||
assert!(!serde_json::to_string(&blocked_stdin)
|
||||
.expect("serialize blocked stdin observation")
|
||||
.contains(BLOCKED_STDIN_SENTINEL));
|
||||
|
||||
let mut post_delivery_output = String::new();
|
||||
for index in 0..20 {
|
||||
let (_, observation) = execute_approved_process_action_for_test(
|
||||
&root,
|
||||
&mut state,
|
||||
AgentRuntimeToolAction {
|
||||
tool: "command.poll".to_string(),
|
||||
reason: Some(format!("协作后继续读取 Supervisor fixture {index}")),
|
||||
input: serde_json::json!({
|
||||
"processId": process_id,
|
||||
"cursor": cursor,
|
||||
"maxChars": 8_000,
|
||||
"waitMs": 500
|
||||
}),
|
||||
},
|
||||
AGENT_RUNTIME_ACTION_EXECUTION_MODE_AUTO,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(observation.status, "ok", "{observation:?}");
|
||||
let detail: Value = serde_json::from_str(
|
||||
observation
|
||||
.detail
|
||||
.as_deref()
|
||||
.expect("post-delivery poll detail"),
|
||||
)
|
||||
.expect("parse post-delivery poll detail");
|
||||
assert_eq!(detail["processId"], process_id);
|
||||
let output = detail["output"].as_str().unwrap_or_default();
|
||||
assert!(!output.contains(BLOCKED_STDIN_SENTINEL), "{output}");
|
||||
post_delivery_output.push_str(output);
|
||||
cursor = detail["nextCursor"]
|
||||
.as_str()
|
||||
.expect("post-delivery poll next cursor")
|
||||
.to_string();
|
||||
if post_delivery_output.contains(POLL_SENTINEL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert!(
|
||||
post_delivery_output.contains(POLL_SENTINEL),
|
||||
"{post_delivery_output}"
|
||||
);
|
||||
assert!(!post_delivery_output.contains(BLOCKED_STDIN_SENTINEL));
|
||||
let transcript_path = root.join(format!(
|
||||
".agent/runtime/process-sessions/{process_id}.output.json"
|
||||
));
|
||||
let transcript = fs::read_to_string(&transcript_path).expect("read process transcript");
|
||||
assert!(transcript.contains(READY_SENTINEL));
|
||||
assert!(!transcript.contains(BLOCKED_STDIN_SENTINEL));
|
||||
|
||||
let (_, terminate_observation) = execute_approved_process_action_for_test(
|
||||
&root,
|
||||
&mut state,
|
||||
AgentRuntimeToolAction {
|
||||
tool: "command.terminate".to_string(),
|
||||
reason: Some("协作后只治理并终止既有持久进程".to_string()),
|
||||
input: serde_json::json!({
|
||||
"processId": process_id,
|
||||
"cursor": cursor
|
||||
}),
|
||||
},
|
||||
AGENT_RUNTIME_ACTION_EXECUTION_MODE_CONFIRMATION,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(
|
||||
terminate_observation.status, "ok",
|
||||
"{terminate_observation:?}"
|
||||
);
|
||||
let terminate_detail: Value = serde_json::from_str(
|
||||
terminate_observation
|
||||
.detail
|
||||
.as_deref()
|
||||
.expect("terminate observation detail"),
|
||||
)
|
||||
.expect("parse terminate detail");
|
||||
assert_eq!(terminate_detail["processId"], process_id);
|
||||
assert!(matches!(
|
||||
terminate_detail["status"].as_str(),
|
||||
Some("terminated" | "exited" | "timed-out" | "output-limit-exceeded")
|
||||
));
|
||||
assert!(!has_active_process_sessions_at(&root).expect("process inactive after terminate"));
|
||||
let terminal_transcript =
|
||||
fs::read_to_string(&transcript_path).expect("read terminal process transcript");
|
||||
assert!(!terminal_transcript.contains(BLOCKED_STDIN_SENTINEL));
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
async fn process_session_agent_runtime_start_audit_failure_fixture() {
|
||||
const AGENT_ID: &str = "code-prototype";
|
||||
|
||||
Reference in New Issue
Block a user