generated-scene: add scheduled monitoring runtime and helper lifecycle hardening

This commit is contained in:
木炎
2026-05-06 09:47:12 +08:00
parent 6cdd71b682
commit 8162118e6d
183 changed files with 103674 additions and 130 deletions

View File

@@ -7,8 +7,10 @@ use sgclaw::generated_scene::analyzer::{
G2FamilyVariant, SceneKind, ToolKind,
};
use sgclaw::generated_scene::generator::{
compute_readiness_for_test, generate_scene_package, resolve_scene_ir_for_test,
GenerateSceneRequest,
compute_readiness_for_test, generate_monitoring_action_detect_preview_package,
generate_scene_package, generate_scheduled_monitoring_action_skill_package,
resolve_scene_ir_for_test, GenerateMonitoringActionPreviewRequest,
GenerateScheduledMonitoringActionSkillRequest, GenerateSceneRequest,
};
use sgclaw::generated_scene::ir::{
ApiEndpointIr, ArtifactContractIr, BootstrapIr, ModeConditionIr, ModeIr, ReadinessIr,
@@ -1381,9 +1383,7 @@ fn generator_escapes_request_mapping_fields_for_valid_toml() {
let output_root = temp_workspace("sgclaw-scene-generator-sweep-078-valid-toml");
generate_scene_package(GenerateSceneRequest {
source_dir: PathBuf::from(
"D:/desk/智能体资料/全量业务场景/一平台场景/线损同期差异报表",
),
source_dir: PathBuf::from("D:/desk/智能体资料/全量业务场景/一平台场景/线损同期差异报表"),
scene_id: "sweep-078-scene".to_string(),
scene_name: "线损同期差异报表".to_string(),
scene_kind: Some(SceneKind::ReportCollection),
@@ -1505,7 +1505,9 @@ fn generator_recovers_local_doc_residual_packages_from_source_evidence() {
scene_info_json: None,
scene_ir_json: None,
})
.unwrap_or_else(|err| panic!("{scene_id} should generate after local-doc residual closure: {err}"));
.unwrap_or_else(|err| {
panic!("{scene_id} should generate after local-doc residual closure: {err}")
});
let generated_report: SceneIr = serde_json::from_str(
&fs::read_to_string(skill_root.join("references/generation-report.json")).unwrap(),
@@ -1525,8 +1527,14 @@ fn generator_recovers_local_doc_residual_packages_from_source_evidence() {
"scene_id={scene_id}; steps={:?}",
generated_report.workflow_steps
);
assert!(skill_root.join("SKILL.toml").exists(), "scene_id={scene_id}");
assert!(skill_root.join("scene.toml").exists(), "scene_id={scene_id}");
assert!(
skill_root.join("SKILL.toml").exists(),
"scene_id={scene_id}"
);
assert!(
skill_root.join("scene.toml").exists(),
"scene_id={scene_id}"
);
}
}
@@ -3048,6 +3056,7 @@ fn generator_blocks_incomplete_g6_host_bridge_contract() {
column_defs: Vec::new(),
confidence: 0.7,
uncertainties: Vec::new(),
monitoring_action_workflow: None,
};
scene_ir.workflow_steps.push(WorkflowStepIr {
step_type: "host_bridge".to_string(),
@@ -3182,6 +3191,7 @@ fn generator_blocks_incomplete_g7_inventory_contract() {
column_defs: Vec::new(),
confidence: 0.7,
uncertainties: Vec::new(),
monitoring_action_workflow: None,
};
let error = generate_scene_package(GenerateSceneRequest {
@@ -3408,6 +3418,7 @@ fn generator_blocks_incomplete_g8_local_doc_pipeline_contract() {
column_defs: Vec::new(),
confidence: 0.7,
uncertainties: Vec::new(),
monitoring_action_workflow: None,
};
let error = generate_scene_package(GenerateSceneRequest {
@@ -3490,6 +3501,7 @@ fn generator_accepts_g8_local_doc_select_data_contract() {
column_defs: Vec::new(),
confidence: 0.7,
uncertainties: Vec::new(),
monitoring_action_workflow: None,
};
let skill_root = generate_scene_package(GenerateSceneRequest {
@@ -3695,6 +3707,192 @@ fn generator_emits_monitoring_template() {
assert!(generated_manifest.contains("category = \"monitoring\""));
}
#[test]
fn generator_emits_monitoring_action_detect_preview_anchor_package() {
let output_root = temp_workspace("sgclaw-monitoring-action-detect-preview");
let skill_root =
generate_monitoring_action_detect_preview_package(GenerateMonitoringActionPreviewRequest {
scene_id: "command-center-fee-control-monitor".to_string(),
scene_name: "指挥中心费控异常监测与处置预览".to_string(),
output_root: output_root.clone(),
source_evidence_json: PathBuf::from(
"tests/fixtures/generated_scene/monitoring_action_source_evidence_extraction_2026-04-21.json",
),
ir_contract_json: PathBuf::from(
"tests/fixtures/generated_scene/monitoring_action_ir_contract_2026-04-21.json",
),
})
.unwrap();
let scene_toml = fs::read_to_string(skill_root.join("scene.toml")).unwrap();
assert!(scene_toml.contains("category = \"monitoring\""));
assert!(scene_toml.contains("suffix = \""));
assert!(scene_toml.contains("[safety]"));
assert!(scene_toml.contains("dry_run_default = true"));
assert!(scene_toml.contains("action_modes_enabled = false"));
assert!(scene_toml.contains("mode = \"detect_preview\""));
assert!(scene_toml.contains("repetCtrlSend"));
let generation_report =
fs::read_to_string(skill_root.join("references/generation-report.json")).unwrap();
let generated_report: SceneIr = serde_json::from_str(&generation_report).unwrap();
assert_eq!(
generated_report.workflow_archetype(),
WorkflowArchetype::MonitoringActionWorkflow
);
let monitoring = generated_report
.monitoring_action_workflow
.as_ref()
.expect("expected monitoring action workflow metadata");
assert_eq!(monitoring.default_mode, "detect_preview");
assert!(monitoring.side_effect_policy.dry_run_default);
assert!(monitoring
.side_effect_policy
.blocked_call_signatures
.iter()
.any(|item| item == "repetCtrlSend"));
let script = fs::read_to_string(skill_root.join("scripts/detect_preview.js")).unwrap();
assert!(script.contains("monitoring_action_workflow"));
assert!(script.contains("detect_preview"));
assert!(!script.contains("repetCtrlSend("));
assert!(!script.contains("mac.sendMessages("));
assert!(!script.contains("mac.callOutLogin("));
assert!(!script.contains("mac.audioPlay("));
assert!(!script.contains("mac.exeTQueue("));
let blocked =
fs::read_to_string(skill_root.join("references/blocked-side-effects.json")).unwrap();
assert!(blocked.contains("repetCtrlSend"));
let test_status = std::process::Command::new("node")
.arg("detect_preview.test.js")
.current_dir(skill_root.join("scripts"))
.status()
.unwrap();
assert!(test_status.success());
}
#[test]
fn generator_emits_scheduled_monitoring_action_skill_package() {
let output_root = temp_workspace("sgclaw-scheduled-monitoring-action-skill");
let skill_root = generate_scheduled_monitoring_action_skill_package(
GenerateScheduledMonitoringActionSkillRequest {
scene_id: "command-center-fee-control-monitor".to_string(),
scene_name: "指挥中心费控异常监测".to_string(),
output_root: output_root.clone(),
source_evidence_json: PathBuf::from(
"tests/fixtures/generated_scene/monitoring_action_source_evidence_extraction_2026-04-21.json",
),
ir_contract_json: PathBuf::from(
"tests/fixtures/generated_scene/scheduled_monitoring_action_ir_contract_2026-04-22.json",
),
trigger_contract_json: PathBuf::from(
"tests/fixtures/generated_scene/scheduled_monitoring_action_trigger_runtime_contract_2026-04-22.json",
),
},
)
.unwrap();
for relative in [
"SKILL.toml",
"SKILL.md",
"scene.toml",
"scripts/detect.js",
"scripts/decide.js",
"scripts/action_plan.js",
"scripts/detect.test.js",
"references/source-evidence.json",
"references/workflow-ir.json",
"references/trigger-contract.json",
"references/platform-dependencies.json",
"references/side-effect-policy.json",
"references/audit-policy.json",
"references/idempotency-policy.json",
"references/generation-report.json",
] {
assert!(
skill_root.join(relative).exists(),
"expected scheduled skill package file {relative}"
);
}
let scene_toml = fs::read_to_string(skill_root.join("scene.toml")).unwrap();
toml::from_str::<toml::Value>(&scene_toml).unwrap();
assert!(scene_toml.contains("kind = \"scheduled_monitoring_action_workflow\""));
assert!(scene_toml.contains("natural_language_primary = false"));
assert!(scene_toml.contains("enabled = [\"dry_run\", \"monitor_only\"]"));
assert!(scene_toml.contains("disabled = [\"active\", \"queue_process\"]"));
assert!(scene_toml.contains("active_enabled = false"));
assert!(scene_toml.contains("queue_process_enabled = false"));
assert!(!scene_toml.contains("[deterministic]"));
let skill_toml = fs::read_to_string(skill_root.join("SKILL.toml")).unwrap();
toml::from_str::<toml::Value>(&skill_toml).unwrap();
assert!(skill_toml.contains("name = \"detect\""));
assert!(skill_toml.contains("name = \"decide\""));
assert!(skill_toml.contains("name = \"action_plan\""));
assert!(skill_toml.contains("kind = \"scheduled_monitoring_action_workflow\""));
let generation_report =
fs::read_to_string(skill_root.join("references/generation-report.json")).unwrap();
let report: serde_json::Value = serde_json::from_str(&generation_report).unwrap();
assert_eq!(report["family"], "scheduled_monitoring_action_workflow");
assert_eq!(report["naturalLanguagePrimary"], false);
assert_eq!(report["safety"]["activeEnabled"], false);
assert_eq!(report["safety"]["queueProcessEnabled"], false);
let side_effect_policy =
fs::read_to_string(skill_root.join("references/side-effect-policy.json")).unwrap();
let side_effect_policy: serde_json::Value = serde_json::from_str(&side_effect_policy).unwrap();
assert!(side_effect_policy["blockedCallSignatures"]
.as_array()
.unwrap()
.iter()
.any(|item| item == "repetCtrlSend"));
let enabled_scripts = [
fs::read_to_string(skill_root.join("scripts/detect.js")).unwrap(),
fs::read_to_string(skill_root.join("scripts/decide.js")).unwrap(),
fs::read_to_string(skill_root.join("scripts/action_plan.js")).unwrap(),
];
assert!(enabled_scripts[0].contains("platformServiceBaseUrl"));
assert!(enabled_scripts[0].contains("postViaPageAxios"));
assert!(enabled_scripts[0].contains("getViaPageAxios"));
assert!(enabled_scripts[0].contains("EmssLib.dataEncrypt_PUB"));
assert!(enabled_scripts[0].contains("readStepTraces"));
assert!(enabled_scripts[0].contains("read_step_timeout_ms"));
assert!(enabled_scripts[0].contains("scheduled monitoring read step"));
for script in enabled_scripts {
for forbidden in [
"repetCtrlSend(",
"mac.sendMessages(",
"mac.callOutLogin(",
"mac.audioPlay(",
"mac.exeTQueue(",
"_this.autoTask(",
"_this.processQueue(",
"setDisposeLog(",
"setMonitorData(",
"setMonitorLog(",
"setSendMessageLog(",
"setAudioPlayLog(",
] {
assert!(
!script.contains(forbidden),
"enabled scheduled script must not contain forbidden executable call {forbidden}"
);
}
}
let test_status = std::process::Command::new("node")
.arg("detect.test.js")
.current_dir(skill_root.join("scripts"))
.status()
.unwrap();
assert!(test_status.success());
}
#[test]
fn generator_preserves_localhost_dependency_as_host_runtime_evidence() {
let analysis = analyze_scene_source(Path::new(
@@ -3850,6 +4048,7 @@ fn build_multi_mode_scene_ir() -> SceneIr {
column_defs: Vec::new(),
confidence: 0.9,
uncertainties: Vec::new(),
monitoring_action_workflow: None,
}
}
@@ -3975,6 +4174,7 @@ fn build_paginated_scene_ir() -> SceneIr {
column_defs: Vec::new(),
confidence: 0.9,
uncertainties: Vec::new(),
monitoring_action_workflow: None,
}
}