Compare commits
5 Commits
956f0c2b68
...
6122b521a8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6122b521a8 | ||
|
|
8162118e6d | ||
|
|
6cdd71b682 | ||
|
|
6c3f3a4002 | ||
|
|
2428e975eb |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -11,3 +11,10 @@ sgclaw_config.json
|
||||
nul
|
||||
target-test/
|
||||
target-zhihu-nav/
|
||||
dist/
|
||||
.codex/
|
||||
node_modules/
|
||||
tmp_*
|
||||
vibe_images/
|
||||
sgclaw_config_zhihu_probe.json
|
||||
examples/
|
||||
|
||||
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -2384,6 +2384,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"tempfile",
|
||||
"thiserror 1.0.69",
|
||||
"tokio",
|
||||
"toml 0.8.23",
|
||||
|
||||
@@ -22,3 +22,6 @@ tungstenite = "0.29"
|
||||
uuid = { version = "1", features = ["v4"] }
|
||||
zip = { version = "0.6.6", default-features = false, features = ["deflate"] }
|
||||
zeroclaw = { package = "zeroclawlabs", path = "third_party/zeroclaw", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,565 @@
|
||||
use std::any::Any;
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::Write;
|
||||
use std::panic::{self, AssertUnwindSafe};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::ExitCode;
|
||||
|
||||
use sgclaw::config::SgClawSettings;
|
||||
use sgclaw::generated_scene::scheduled_monitoring_runtime::{
|
||||
run_scheduled_monitoring_command_adapter, run_scheduled_monitoring_skill_command_adapter,
|
||||
run_scheduled_monitoring_skill_command_adapter_many,
|
||||
ScheduledMonitoringCommandAdapterRequest, ScheduledMonitoringSkillCommandAdapterRequest,
|
||||
ScheduledMonitoringSkillCommandAdapterManyRequest,
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
fn main() -> ExitCode {
|
||||
if let Err(err) = sgclaw::service::run() {
|
||||
eprintln!("sg_claw failed: {err}");
|
||||
return ExitCode::FAILURE;
|
||||
let args: Vec<String> = std::env::args().skip(1).collect();
|
||||
let args_for_log = args.clone();
|
||||
let working_dir = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
|
||||
let exit_code =
|
||||
execute_with_top_level_logging(&working_dir, &args_for_log, || run_main(args));
|
||||
ExitCode::from(exit_code as u8)
|
||||
}
|
||||
|
||||
fn run_main(args: Vec<String>) -> i32 {
|
||||
match parse_scheduled_monitoring_cli(args) {
|
||||
Ok(Some(config)) => {
|
||||
let result = if let Some(skills_dir) = config.skills_dir.as_ref() {
|
||||
if config.trigger_paths.len() == 1 {
|
||||
run_scheduled_monitoring_skill_command_adapter(
|
||||
ScheduledMonitoringSkillCommandAdapterRequest {
|
||||
trigger_path: &config.trigger_paths[0],
|
||||
skills_dir,
|
||||
config_path: config.config_path.as_deref(),
|
||||
output_path: &config.output_path,
|
||||
watch: config.watch,
|
||||
max_runs: config.max_runs,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
run_scheduled_monitoring_skill_command_adapter_many(
|
||||
ScheduledMonitoringSkillCommandAdapterManyRequest {
|
||||
trigger_paths: &config.trigger_paths,
|
||||
skills_dir,
|
||||
config_path: config.config_path.as_deref(),
|
||||
output_path: &config.output_path,
|
||||
watch: config.watch,
|
||||
max_runs: config.max_runs,
|
||||
},
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if config.trigger_paths.len() != 1 {
|
||||
eprintln!(
|
||||
"scheduled monitoring trigger failed: multiple --scheduled-monitoring-trigger values require --skills-dir"
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
run_scheduled_monitoring_command_adapter(ScheduledMonitoringCommandAdapterRequest {
|
||||
trigger_path: &config.trigger_paths[0],
|
||||
contract_path: &config.contract_path,
|
||||
preview_fixtures_path: &config.fixtures_path,
|
||||
output_path: &config.output_path,
|
||||
})
|
||||
};
|
||||
match result {
|
||||
Ok(_) => return 0,
|
||||
Err(err) => {
|
||||
eprintln!("scheduled monitoring trigger failed: {err}");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(None) => {}
|
||||
Err(err) => {
|
||||
eprintln!("sg_claw argument error: {err}");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
ExitCode::SUCCESS
|
||||
if let Err(err) = sgclaw::service::run() {
|
||||
eprintln!("sg_claw failed: {err}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
fn execute_with_top_level_logging<F>(working_dir: &Path, args: &[String], runner: F) -> i32
|
||||
where
|
||||
F: FnOnce() -> i32,
|
||||
{
|
||||
let started_at = chrono::Local::now().to_rfc3339();
|
||||
let outcome = panic::catch_unwind(AssertUnwindSafe(runner));
|
||||
|
||||
match outcome {
|
||||
Ok(exit_code) => {
|
||||
append_watch_exit_log(
|
||||
working_dir,
|
||||
&started_at,
|
||||
args,
|
||||
"process_exit",
|
||||
if exit_code == 0 { "success" } else { "failure" },
|
||||
exit_code,
|
||||
None,
|
||||
);
|
||||
exit_code
|
||||
}
|
||||
Err(payload) => {
|
||||
let panic_message = extract_panic_message(payload.as_ref());
|
||||
append_watch_exit_log(
|
||||
working_dir,
|
||||
&started_at,
|
||||
args,
|
||||
"process_panic",
|
||||
"panic",
|
||||
1,
|
||||
Some(&panic_message),
|
||||
);
|
||||
eprintln!("sg_claw panicked: {panic_message}");
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_panic_message(payload: &(dyn Any + Send)) -> String {
|
||||
if let Some(message) = payload.downcast_ref::<String>() {
|
||||
return message.clone();
|
||||
}
|
||||
if let Some(message) = payload.downcast_ref::<&str>() {
|
||||
return (*message).to_string();
|
||||
}
|
||||
"unknown panic payload".to_string()
|
||||
}
|
||||
|
||||
fn append_watch_exit_log(
|
||||
working_dir: &Path,
|
||||
started_at: &str,
|
||||
args: &[String],
|
||||
event: &str,
|
||||
outcome: &str,
|
||||
exit_code: i32,
|
||||
message: Option<&str>,
|
||||
) {
|
||||
let log_path = working_dir.join("results").join("sgclaw-watch-fatal.log");
|
||||
if let Some(parent) = log_path.parent() {
|
||||
let _ = fs::create_dir_all(parent);
|
||||
}
|
||||
|
||||
let payload = json!({
|
||||
"ts": chrono::Local::now().to_rfc3339(),
|
||||
"event": event,
|
||||
"outcome": outcome,
|
||||
"exitCode": exit_code,
|
||||
"startedAt": started_at,
|
||||
"cwd": working_dir.display().to_string(),
|
||||
"watch": args.iter().any(|arg| arg == "--watch"),
|
||||
"args": args,
|
||||
"message": message,
|
||||
});
|
||||
|
||||
if let Ok(mut file) = OpenOptions::new().create(true).append(true).open(&log_path) {
|
||||
let _ = writeln!(file, "{payload}");
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct ScheduledMonitoringCliConfig {
|
||||
trigger_paths: Vec<PathBuf>,
|
||||
output_path: PathBuf,
|
||||
skills_dir: Option<PathBuf>,
|
||||
config_path: Option<PathBuf>,
|
||||
contract_path: PathBuf,
|
||||
fixtures_path: PathBuf,
|
||||
watch: bool,
|
||||
max_runs: Option<usize>,
|
||||
}
|
||||
|
||||
fn parse_scheduled_monitoring_cli(
|
||||
args: Vec<String>,
|
||||
) -> Result<Option<ScheduledMonitoringCliConfig>, String> {
|
||||
let mut trigger_paths = Vec::new();
|
||||
let mut output_path = None;
|
||||
let mut skills_dir = None;
|
||||
let mut config_path = None;
|
||||
let mut contract_path = None;
|
||||
let mut fixtures_path = None;
|
||||
let mut watch = false;
|
||||
let mut max_runs = None;
|
||||
|
||||
let mut iter = args.into_iter();
|
||||
while let Some(arg) = iter.next() {
|
||||
match arg.as_str() {
|
||||
"--scheduled-monitoring-trigger" => {
|
||||
trigger_paths.push(PathBuf::from(next_arg(
|
||||
&mut iter,
|
||||
"--scheduled-monitoring-trigger",
|
||||
)?));
|
||||
}
|
||||
"--output" => {
|
||||
output_path = Some(PathBuf::from(next_arg(&mut iter, "--output")?));
|
||||
}
|
||||
"--skills-dir" => {
|
||||
skills_dir = Some(PathBuf::from(next_arg(&mut iter, "--skills-dir")?));
|
||||
}
|
||||
"--config-path" => {
|
||||
config_path = Some(resolve_process_path(PathBuf::from(next_arg(
|
||||
&mut iter,
|
||||
"--config-path",
|
||||
)?)));
|
||||
}
|
||||
"--scheduled-monitoring-contract" => {
|
||||
contract_path = Some(PathBuf::from(next_arg(
|
||||
&mut iter,
|
||||
"--scheduled-monitoring-contract",
|
||||
)?));
|
||||
}
|
||||
"--scheduled-monitoring-fixtures" => {
|
||||
fixtures_path = Some(PathBuf::from(next_arg(
|
||||
&mut iter,
|
||||
"--scheduled-monitoring-fixtures",
|
||||
)?));
|
||||
}
|
||||
"--watch" => {
|
||||
watch = true;
|
||||
}
|
||||
"--max-runs" => {
|
||||
max_runs = Some(parse_usize_arg(&mut iter, "--max-runs")?);
|
||||
}
|
||||
_ => {
|
||||
if let Some(value) = arg.strip_prefix("--scheduled-monitoring-trigger=") {
|
||||
trigger_paths.push(PathBuf::from(value));
|
||||
} else if let Some(value) = arg.strip_prefix("--output=") {
|
||||
output_path = Some(PathBuf::from(value));
|
||||
} else if let Some(value) = arg.strip_prefix("--skills-dir=") {
|
||||
skills_dir = Some(PathBuf::from(value));
|
||||
} else if let Some(value) = arg.strip_prefix("--config-path=") {
|
||||
config_path = Some(resolve_process_path(PathBuf::from(value)));
|
||||
} else if let Some(value) = arg.strip_prefix("--scheduled-monitoring-contract=") {
|
||||
contract_path = Some(PathBuf::from(value));
|
||||
} else if let Some(value) = arg.strip_prefix("--scheduled-monitoring-fixtures=") {
|
||||
fixtures_path = Some(PathBuf::from(value));
|
||||
} else if let Some(value) = arg.strip_prefix("--max-runs=") {
|
||||
max_runs = Some(
|
||||
value
|
||||
.parse::<usize>()
|
||||
.map_err(|_| format!("invalid value for --max-runs: {value}"))?,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if trigger_paths.is_empty() {
|
||||
if watch {
|
||||
if let Some(config_path) = config_path.as_deref() {
|
||||
if let Some(settings) =
|
||||
SgClawSettings::load(Some(config_path)).map_err(|err| err.to_string())?
|
||||
{
|
||||
trigger_paths = settings
|
||||
.scheduled_monitoring_watch_tasks
|
||||
.into_iter()
|
||||
.map(PathBuf::from)
|
||||
.collect();
|
||||
}
|
||||
}
|
||||
}
|
||||
if trigger_paths.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
let output_path = output_path.ok_or_else(|| {
|
||||
"missing required --output for --scheduled-monitoring-trigger".to_string()
|
||||
})?;
|
||||
|
||||
Ok(Some(ScheduledMonitoringCliConfig {
|
||||
trigger_paths,
|
||||
output_path,
|
||||
skills_dir,
|
||||
config_path,
|
||||
contract_path: contract_path.unwrap_or_else(|| {
|
||||
PathBuf::from(
|
||||
"tests/fixtures/generated_scene/scheduled_monitoring_action_trigger_runtime_contract_2026-04-22.json",
|
||||
)
|
||||
}),
|
||||
fixtures_path: fixtures_path.unwrap_or_else(|| {
|
||||
PathBuf::from(
|
||||
"tests/fixtures/generated_scene/monitoring_action_mock_validation_fixtures_2026-04-22.json",
|
||||
)
|
||||
}),
|
||||
watch,
|
||||
max_runs,
|
||||
}))
|
||||
}
|
||||
|
||||
fn next_arg(iter: &mut impl Iterator<Item = String>, flag: &str) -> Result<String, String> {
|
||||
iter.next()
|
||||
.ok_or_else(|| format!("missing value for {flag}"))
|
||||
}
|
||||
|
||||
fn parse_usize_arg(iter: &mut impl Iterator<Item = String>, flag: &str) -> Result<usize, String> {
|
||||
let value = next_arg(iter, flag)?;
|
||||
value
|
||||
.parse::<usize>()
|
||||
.map_err(|_| format!("invalid value for {flag}: {value}"))
|
||||
}
|
||||
|
||||
fn resolve_process_path(path: PathBuf) -> PathBuf {
|
||||
if path.is_absolute() {
|
||||
path
|
||||
} else {
|
||||
std::env::current_dir()
|
||||
.unwrap_or_else(|_| PathBuf::from("."))
|
||||
.join(path)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{execute_with_top_level_logging, parse_scheduled_monitoring_cli};
|
||||
use serde_json::Value;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
fn temp_json_file(prefix: &str, body: &str) -> PathBuf {
|
||||
let nanos = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let path = std::env::temp_dir().join(format!("{prefix}-{nanos}.json"));
|
||||
fs::write(&path, body).unwrap();
|
||||
path
|
||||
}
|
||||
|
||||
fn temp_workspace(prefix: &str) -> PathBuf {
|
||||
let nanos = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let path = std::env::temp_dir().join(format!("{prefix}-{nanos}"));
|
||||
fs::create_dir_all(path.join("results")).unwrap();
|
||||
path
|
||||
}
|
||||
|
||||
fn read_exit_log_line(workspace: &Path) -> Value {
|
||||
let raw = fs::read_to_string(workspace.join("results").join("sgclaw-watch-fatal.log"))
|
||||
.unwrap();
|
||||
serde_json::from_str(raw.lines().last().unwrap()).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scheduled_monitoring_cli_is_absent_by_default() {
|
||||
assert!(parse_scheduled_monitoring_cli(vec![
|
||||
"--config-path".to_string(),
|
||||
"sgclaw_config.json".to_string(),
|
||||
])
|
||||
.unwrap()
|
||||
.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scheduled_monitoring_cli_requires_output() {
|
||||
let error = parse_scheduled_monitoring_cli(vec![
|
||||
"--scheduled-monitoring-trigger".to_string(),
|
||||
"trigger.json".to_string(),
|
||||
])
|
||||
.unwrap_err();
|
||||
|
||||
assert!(error.contains("missing required --output"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scheduled_monitoring_cli_parses_trigger_output_and_defaults() {
|
||||
let config = parse_scheduled_monitoring_cli(vec![
|
||||
"--scheduled-monitoring-trigger".to_string(),
|
||||
"trigger.json".to_string(),
|
||||
"--output".to_string(),
|
||||
"run-record.json".to_string(),
|
||||
])
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(config.trigger_paths, vec![PathBuf::from("trigger.json")]);
|
||||
assert_eq!(config.output_path, PathBuf::from("run-record.json"));
|
||||
assert_eq!(config.skills_dir, None);
|
||||
assert!(config
|
||||
.contract_path
|
||||
.ends_with("scheduled_monitoring_action_trigger_runtime_contract_2026-04-22.json"));
|
||||
assert!(config
|
||||
.fixtures_path
|
||||
.ends_with("monitoring_action_mock_validation_fixtures_2026-04-22.json"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scheduled_monitoring_cli_parses_optional_skills_dir() {
|
||||
let config = parse_scheduled_monitoring_cli(vec![
|
||||
"--scheduled-monitoring-trigger".to_string(),
|
||||
"trigger.json".to_string(),
|
||||
"--output".to_string(),
|
||||
"run-record.json".to_string(),
|
||||
"--skills-dir".to_string(),
|
||||
"skills".to_string(),
|
||||
"--config-path".to_string(),
|
||||
"sgclaw_config.json".to_string(),
|
||||
])
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(config.skills_dir, Some(PathBuf::from("skills")));
|
||||
assert!(
|
||||
config
|
||||
.config_path
|
||||
.as_ref()
|
||||
.is_some_and(|path| path.ends_with("sgclaw_config.json"))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scheduled_monitoring_cli_parses_watch_and_max_runs() {
|
||||
let config = parse_scheduled_monitoring_cli(vec![
|
||||
"--scheduled-monitoring-trigger".to_string(),
|
||||
"trigger.json".to_string(),
|
||||
"--output".to_string(),
|
||||
"run-record.json".to_string(),
|
||||
"--watch".to_string(),
|
||||
"--max-runs".to_string(),
|
||||
"2".to_string(),
|
||||
])
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert!(config.watch);
|
||||
assert_eq!(config.max_runs, Some(2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scheduled_monitoring_cli_parses_multiple_triggers() {
|
||||
let config = parse_scheduled_monitoring_cli(vec![
|
||||
"--scheduled-monitoring-trigger".to_string(),
|
||||
"fee.json".to_string(),
|
||||
"--scheduled-monitoring-trigger".to_string(),
|
||||
"archive.json".to_string(),
|
||||
"--output".to_string(),
|
||||
"watch-run-record.json".to_string(),
|
||||
"--skills-dir".to_string(),
|
||||
"skills".to_string(),
|
||||
"--watch".to_string(),
|
||||
])
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
config.trigger_paths,
|
||||
vec![PathBuf::from("fee.json"), PathBuf::from("archive.json")]
|
||||
);
|
||||
assert_eq!(config.output_path, PathBuf::from("watch-run-record.json"));
|
||||
assert_eq!(config.skills_dir, Some(PathBuf::from("skills")));
|
||||
assert!(config.watch);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scheduled_monitoring_cli_loads_watch_tasks_from_config_when_watch_has_no_trigger() {
|
||||
let config_path = temp_json_file(
|
||||
"sgclaw-watch-config",
|
||||
r#"{
|
||||
"apiKey": "sk-test",
|
||||
"baseUrl": "https://api.deepseek.com",
|
||||
"model": "deepseek-chat",
|
||||
"scheduledMonitoring": {
|
||||
"watchTasks": [
|
||||
"handoff/trigger.read_only.template.json",
|
||||
"handoff/trigger.archive_workorder_grid_push.template.json"
|
||||
]
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
|
||||
let config = parse_scheduled_monitoring_cli(vec![
|
||||
"--config-path".to_string(),
|
||||
config_path.to_string_lossy().into_owned(),
|
||||
"--skills-dir".to_string(),
|
||||
"skills".to_string(),
|
||||
"--output".to_string(),
|
||||
"watch-run-record.json".to_string(),
|
||||
"--watch".to_string(),
|
||||
])
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
config.trigger_paths,
|
||||
vec![
|
||||
PathBuf::from("handoff/trigger.read_only.template.json"),
|
||||
PathBuf::from("handoff/trigger.archive_workorder_grid_push.template.json")
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scheduled_monitoring_cli_explicit_trigger_overrides_config_watch_tasks() {
|
||||
let config_path = temp_json_file(
|
||||
"sgclaw-watch-config-override",
|
||||
r#"{
|
||||
"apiKey": "sk-test",
|
||||
"baseUrl": "https://api.deepseek.com",
|
||||
"model": "deepseek-chat",
|
||||
"scheduledMonitoring": {
|
||||
"watchTasks": [
|
||||
"handoff/trigger.read_only.template.json",
|
||||
"handoff/trigger.archive_workorder_grid_push.template.json"
|
||||
]
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
|
||||
let config = parse_scheduled_monitoring_cli(vec![
|
||||
"--config-path".to_string(),
|
||||
config_path.to_string_lossy().into_owned(),
|
||||
"--scheduled-monitoring-trigger".to_string(),
|
||||
"only-this.json".to_string(),
|
||||
"--output".to_string(),
|
||||
"watch-run-record.json".to_string(),
|
||||
"--watch".to_string(),
|
||||
])
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(config.trigger_paths, vec![PathBuf::from("only-this.json")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn top_level_logging_records_non_zero_exit() {
|
||||
let workspace = temp_workspace("sgclaw-exit-log");
|
||||
let args = vec!["--watch".to_string(), "--output".to_string(), "watch-run-record.json".to_string()];
|
||||
|
||||
let exit_code = execute_with_top_level_logging(&workspace, &args, || 1);
|
||||
|
||||
assert_eq!(exit_code, 1);
|
||||
let payload = read_exit_log_line(&workspace);
|
||||
assert_eq!(payload["event"], "process_exit");
|
||||
assert_eq!(payload["outcome"], "failure");
|
||||
assert_eq!(payload["exitCode"], 1);
|
||||
assert_eq!(payload["watch"], true);
|
||||
assert_eq!(payload["args"][0], "--watch");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn top_level_logging_records_panic_as_failure() {
|
||||
let workspace = temp_workspace("sgclaw-panic-log");
|
||||
let args = vec!["--watch".to_string()];
|
||||
|
||||
let exit_code = execute_with_top_level_logging(&workspace, &args, || {
|
||||
panic!("boom");
|
||||
});
|
||||
|
||||
assert_eq!(exit_code, 1);
|
||||
let payload = read_exit_log_line(&workspace);
|
||||
assert_eq!(payload["event"], "process_panic");
|
||||
assert_eq!(payload["outcome"], "panic");
|
||||
assert_eq!(payload["exitCode"], 1);
|
||||
assert_eq!(payload["message"], "boom");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,21 +442,69 @@ fn build_get_text_js(source_url: &str, selector: &str) -> String {
|
||||
|
||||
fn build_eval_js(source_url: &str, script: &str) -> String {
|
||||
let escaped_source_url = escape_js_single_quoted(source_url);
|
||||
let escaped_script = escape_js_single_quoted(script);
|
||||
let callback = EVAL_CALLBACK_NAME;
|
||||
let events_url = escape_js_single_quoted(&events_endpoint_url(source_url));
|
||||
let script_len = script.len();
|
||||
let eval_sentinel = format!("sgclaw-eval-wrapper-{script_len}");
|
||||
eprintln!(
|
||||
"[callback_backend] build_eval_js source_url={} script_len={} sentinel={}",
|
||||
source_url,
|
||||
script_len,
|
||||
eval_sentinel
|
||||
);
|
||||
|
||||
format!(
|
||||
"(function(){{try{{\
|
||||
var v=(function(){{return {script}}})();\
|
||||
function _s(v){{\
|
||||
var t=(typeof v==='string')?v:JSON.stringify(v);\
|
||||
var __sgclawEvalSentinel='{eval_sentinel}';\
|
||||
function _j(v){{\
|
||||
if(typeof v==='string')return v;\
|
||||
try{{return JSON.stringify(v);}}catch(_){{return String(v);}}\
|
||||
}}\
|
||||
function _e(err){{\
|
||||
if(!err)return{{message:'unknown error'}};\
|
||||
if(typeof err==='string')return{{message:err}};\
|
||||
return {{\
|
||||
name: err.name||'Error',\
|
||||
message: err.message||String(err),\
|
||||
stack: err.stack||'',\
|
||||
code: err.code||'',\
|
||||
stage: err.stage||'',\
|
||||
url: err.url||'',\
|
||||
timeoutMs: err.timeoutMs||0,\
|
||||
trace: err.trace||null\
|
||||
}};\
|
||||
}}\
|
||||
function _emit(payload){{\
|
||||
var t=_j(payload);\
|
||||
try{{callBackJsToCpp('{escaped_source_url}@_@'+window.location.href+'@_@{callback}@_@sgBrowserExcuteJsCodeByDomain@_@'+(t??''))}}catch(_){{}}\
|
||||
var j=JSON.stringify({{type:'callback',callback:'{callback}',request_url:'{escaped_source_url}',payload:{{value:(t??'')}}}});\
|
||||
var j=JSON.stringify({{type:'callback',callback:'{callback}',request_url:'{escaped_source_url}',payload:payload}});\
|
||||
try{{var r=new XMLHttpRequest();r.open('POST','{events_url}',true);r.setRequestHeader('Content-Type','application/json');r.send(j)}}catch(_){{}}\
|
||||
try{{navigator.sendBeacon('{events_url}',new Blob([j],{{type:'application/json'}}))}}catch(_){{}}\
|
||||
}}\
|
||||
if(v&&typeof v.then==='function'){{v.then(_s).catch(function(){{}});}}else{{_s(v);}}\
|
||||
}}catch(e){{}}}})()"
|
||||
var _compiled;\
|
||||
try{{\
|
||||
_compiled = new Function('return ({escaped_script});');\
|
||||
}}catch(compileErr){{\
|
||||
_emit({{value:null,error:_e(compileErr),phase:'eval_compile_failed'}});\
|
||||
return;\
|
||||
}}\
|
||||
var v;\
|
||||
try{{\
|
||||
v = _compiled();\
|
||||
}}catch(runErr){{\
|
||||
_emit({{value:null,error:_e(runErr),phase:'eval_runtime_failed'}});\
|
||||
return;\
|
||||
}}\
|
||||
function _s(v){{_emit({{value:v??null,error:null}});}}\
|
||||
function _f(err){{_emit({{value:null,error:_e(err)}});}}\
|
||||
if(v&&typeof v.then==='function'){{v.then(_s).catch(_f);}}else{{_s(v);}}\
|
||||
}}catch(e){{\
|
||||
var payload={{value:null,error:{{name:e&&e.name||'Error',message:e&&e.message||String(e),stack:e&&e.stack||''}}}};\
|
||||
try{{callBackJsToCpp('{escaped_source_url}@_@'+window.location.href+'@_@{callback}@_@sgBrowserExcuteJsCodeByDomain@_@'+JSON.stringify(payload))}}catch(_){{}}\
|
||||
try{{var j=JSON.stringify({{type:'callback',callback:'{callback}',request_url:'{escaped_source_url}',payload:payload}});var r=new XMLHttpRequest();r.open('POST','{events_url}',true);r.setRequestHeader('Content-Type','application/json');r.send(j)}}catch(_){{}}\
|
||||
try{{navigator.sendBeacon('{events_url}',new Blob([JSON.stringify({{type:'callback',callback:'{callback}',request_url:'{escaped_source_url}',payload:payload}})],{{type:'application/json'}}))}}catch(_){{}}\
|
||||
}}}})()"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
297
src/browser/callback_host_lifecycle_log.rs
Normal file
297
src/browser/callback_host_lifecycle_log.rs
Normal file
@@ -0,0 +1,297 @@
|
||||
use chrono::Local;
|
||||
use serde::Serialize;
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::io::{BufWriter, Write};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Mutex;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct CallbackHostLifecycleLogger {
|
||||
path: PathBuf,
|
||||
writer: Mutex<Option<BufWriter<File>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct SharedLifecycleFields {
|
||||
pub ts: String,
|
||||
pub event: String,
|
||||
pub host_instance_id: String,
|
||||
pub run_id: String,
|
||||
pub skill_id: String,
|
||||
pub helper_url: String,
|
||||
pub listener_port: u16,
|
||||
pub thread: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct HostCreateEvent {
|
||||
#[serde(flatten)]
|
||||
pub shared: SharedLifecycleFields,
|
||||
pub browser_ws_url: String,
|
||||
pub use_hidden_domain: bool,
|
||||
pub request_url: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct ReadyStateTransitionEvent {
|
||||
#[serde(flatten)]
|
||||
pub shared: SharedLifecycleFields,
|
||||
pub helper_loaded: bool,
|
||||
pub ready: bool,
|
||||
pub source: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct CommandIssueEvent {
|
||||
#[serde(flatten)]
|
||||
pub shared: SharedLifecycleFields,
|
||||
pub seq: u64,
|
||||
pub action: String,
|
||||
pub script_len: usize,
|
||||
pub expected_domain: String,
|
||||
pub request_url: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct PollErrorEvent {
|
||||
#[serde(flatten)]
|
||||
pub shared: SharedLifecycleFields,
|
||||
pub seq: Option<u64>,
|
||||
pub message: String,
|
||||
pub elapsed_ms: Option<u128>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct HostDropEvent {
|
||||
#[serde(flatten)]
|
||||
pub shared: SharedLifecycleFields,
|
||||
pub uptime_ms: u128,
|
||||
pub matched_callback_count: u64,
|
||||
pub unmatched_callback_count: u64,
|
||||
pub poll_error_count: u64,
|
||||
pub helper_loaded: bool,
|
||||
pub ready: bool,
|
||||
pub drop_join_ok: bool,
|
||||
}
|
||||
|
||||
impl CallbackHostLifecycleLogger {
|
||||
pub(crate) fn new(path: PathBuf) -> Self {
|
||||
Self {
|
||||
path,
|
||||
writer: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn now_local_rfc3339() -> String {
|
||||
Local::now().to_rfc3339()
|
||||
}
|
||||
|
||||
pub(crate) fn write_host_create(&self, event: HostCreateEvent) -> std::io::Result<()> {
|
||||
self.write_line(&event)
|
||||
}
|
||||
|
||||
pub(crate) fn write_ready_state_transition(
|
||||
&self,
|
||||
event: ReadyStateTransitionEvent,
|
||||
) -> std::io::Result<()> {
|
||||
self.write_line(&event)
|
||||
}
|
||||
|
||||
pub(crate) fn write_command_issue(&self, event: CommandIssueEvent) -> std::io::Result<()> {
|
||||
self.write_line(&event)
|
||||
}
|
||||
|
||||
pub(crate) fn write_poll_error(&self, event: PollErrorEvent) -> std::io::Result<()> {
|
||||
self.write_line(&event)
|
||||
}
|
||||
|
||||
pub(crate) fn write_host_drop(&self, event: HostDropEvent) -> std::io::Result<()> {
|
||||
self.write_line(&event)
|
||||
}
|
||||
|
||||
fn ensure_writer(&self) -> std::io::Result<()> {
|
||||
let mut guard = self.writer.lock().unwrap();
|
||||
if guard.is_none() {
|
||||
if let Some(parent) = self.path.parent() {
|
||||
fs::create_dir_all(parent)?;
|
||||
}
|
||||
let file = OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(&self.path)?;
|
||||
*guard = Some(BufWriter::new(file));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_line<T: Serialize>(&self, value: &T) -> std::io::Result<()> {
|
||||
self.ensure_writer()?;
|
||||
let mut guard = self.writer.lock().unwrap();
|
||||
let writer = guard.as_mut().expect("writer initialized");
|
||||
serde_json::to_writer(&mut *writer, value)?;
|
||||
writer.write_all(b"\n")?;
|
||||
writer.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{
|
||||
CallbackHostLifecycleLogger, CommandIssueEvent, HostCreateEvent, HostDropEvent,
|
||||
PollErrorEvent, ReadyStateTransitionEvent,
|
||||
SharedLifecycleFields,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn lifecycle_log_writes_host_create_event_with_shared_fields() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let log_path = dir.path().join("callback-host-lifecycle.ndjson");
|
||||
|
||||
let logger = CallbackHostLifecycleLogger::new(log_path.clone());
|
||||
logger
|
||||
.write_host_create(HostCreateEvent {
|
||||
shared: SharedLifecycleFields {
|
||||
ts: "2026-04-29T21:15:32.123+08:00".to_string(),
|
||||
event: "host_create".to_string(),
|
||||
host_instance_id: "cbh-000123".to_string(),
|
||||
run_id: "run-001".to_string(),
|
||||
skill_id: "command-center-fee-control-monitor".to_string(),
|
||||
helper_url: "http://127.0.0.1:60882/sgclaw/browser-helper.html".to_string(),
|
||||
listener_port: 60882,
|
||||
thread: "callback-host".to_string(),
|
||||
},
|
||||
browser_ws_url: "ws://127.0.0.1:12345".to_string(),
|
||||
use_hidden_domain: true,
|
||||
request_url: "http://yx.gs.sgcc.com.cn/".to_string(),
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let text = std::fs::read_to_string(log_path).unwrap();
|
||||
assert!(text.contains("\"event\":\"host_create\""));
|
||||
assert!(text.contains("\"hostInstanceId\":\"cbh-000123\""));
|
||||
assert!(text.contains("\"listenerPort\":60882"));
|
||||
assert!(text.contains("\"browserWsUrl\":\"ws://127.0.0.1:12345\""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lifecycle_log_writes_ready_state_transition_event() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let log_path = dir.path().join("callback-host-lifecycle.ndjson");
|
||||
|
||||
let logger = CallbackHostLifecycleLogger::new(log_path.clone());
|
||||
logger
|
||||
.write_ready_state_transition(ReadyStateTransitionEvent {
|
||||
shared: SharedLifecycleFields {
|
||||
ts: "2026-04-29T21:15:32.441+08:00".to_string(),
|
||||
event: "ready_state_transition".to_string(),
|
||||
host_instance_id: "cbh-000123".to_string(),
|
||||
run_id: "run-001".to_string(),
|
||||
skill_id: "command-center-fee-control-monitor".to_string(),
|
||||
helper_url: "http://127.0.0.1:60882/sgclaw/browser-helper.html".to_string(),
|
||||
listener_port: 60882,
|
||||
thread: "callback-host".to_string(),
|
||||
},
|
||||
helper_loaded: true,
|
||||
ready: true,
|
||||
source: "ready_endpoint_hit".to_string(),
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let text = std::fs::read_to_string(log_path).unwrap();
|
||||
assert!(text.contains("\"event\":\"ready_state_transition\""));
|
||||
assert!(text.contains("\"helperLoaded\":true"));
|
||||
assert!(text.contains("\"ready\":true"));
|
||||
assert!(text.contains("\"source\":\"ready_endpoint_hit\""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lifecycle_log_writes_command_issue_and_poll_error_events() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let log_path = dir.path().join("callback-host-lifecycle.ndjson");
|
||||
|
||||
let logger = CallbackHostLifecycleLogger::new(log_path.clone());
|
||||
logger
|
||||
.write_command_issue(CommandIssueEvent {
|
||||
shared: SharedLifecycleFields {
|
||||
ts: "2026-04-29T21:15:33.018+08:00".to_string(),
|
||||
event: "command_issue".to_string(),
|
||||
host_instance_id: "cbh-000123".to_string(),
|
||||
run_id: "run-001".to_string(),
|
||||
skill_id: "command-center-fee-control-monitor".to_string(),
|
||||
helper_url: "http://127.0.0.1:60882/sgclaw/browser-helper.html".to_string(),
|
||||
listener_port: 60882,
|
||||
thread: "callback-host".to_string(),
|
||||
},
|
||||
seq: 2,
|
||||
action: "eval".to_string(),
|
||||
script_len: 29470,
|
||||
expected_domain: "yx.gs.sgcc.com.cn".to_string(),
|
||||
request_url: "http://127.0.0.1:60882/sgclaw/browser-helper.html".to_string(),
|
||||
})
|
||||
.unwrap();
|
||||
logger
|
||||
.write_poll_error(PollErrorEvent {
|
||||
shared: SharedLifecycleFields {
|
||||
ts: "2026-04-29T21:15:36.566+08:00".to_string(),
|
||||
event: "poll_error".to_string(),
|
||||
host_instance_id: "cbh-000123".to_string(),
|
||||
run_id: "run-001".to_string(),
|
||||
skill_id: "command-center-fee-control-monitor".to_string(),
|
||||
helper_url: "http://127.0.0.1:60882/sgclaw/browser-helper.html".to_string(),
|
||||
listener_port: 60882,
|
||||
thread: "callback-host".to_string(),
|
||||
},
|
||||
seq: Some(2),
|
||||
message: "Failed to fetch".to_string(),
|
||||
elapsed_ms: Some(3548),
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let text = std::fs::read_to_string(log_path).unwrap();
|
||||
assert!(text.contains("\"event\":\"command_issue\""));
|
||||
assert!(text.contains("\"scriptLen\":29470"));
|
||||
assert!(text.contains("\"event\":\"poll_error\""));
|
||||
assert!(text.contains("\"message\":\"Failed to fetch\""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lifecycle_log_writes_host_drop_event() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let log_path = dir.path().join("callback-host-lifecycle.ndjson");
|
||||
|
||||
let logger = CallbackHostLifecycleLogger::new(log_path.clone());
|
||||
logger
|
||||
.write_host_drop(HostDropEvent {
|
||||
shared: SharedLifecycleFields {
|
||||
ts: "2026-04-29T21:15:58.020+08:00".to_string(),
|
||||
event: "host_drop".to_string(),
|
||||
host_instance_id: "cbh-000123".to_string(),
|
||||
run_id: "run-001".to_string(),
|
||||
skill_id: "command-center-fee-control-monitor".to_string(),
|
||||
helper_url: "http://127.0.0.1:60882/sgclaw/browser-helper.html".to_string(),
|
||||
listener_port: 60882,
|
||||
thread: "callback-host".to_string(),
|
||||
},
|
||||
uptime_ms: 23340,
|
||||
matched_callback_count: 2,
|
||||
unmatched_callback_count: 14,
|
||||
poll_error_count: 11,
|
||||
helper_loaded: true,
|
||||
ready: true,
|
||||
drop_join_ok: true,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let text = std::fs::read_to_string(log_path).unwrap();
|
||||
assert!(text.contains("\"event\":\"host_drop\""));
|
||||
assert!(text.contains("\"uptimeMs\":23340"));
|
||||
assert!(text.contains("\"dropJoinOk\":true"));
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ pub mod bridge_contract;
|
||||
pub mod bridge_transport;
|
||||
pub mod callback_backend;
|
||||
pub(crate) mod callback_host;
|
||||
pub(crate) mod callback_host_lifecycle_log;
|
||||
mod pipe_backend;
|
||||
pub mod ws_backend;
|
||||
pub mod ws_probe;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::fs;
|
||||
use std::net::TcpStream;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -83,24 +84,23 @@ pub fn parse_probe_args(args: &[String]) -> Result<ProbeCliConfig, ProbeError> {
|
||||
let value = args
|
||||
.get(index)
|
||||
.ok_or_else(|| ProbeError::Args("missing value for --step".to_string()))?;
|
||||
let (label, payload) = value.split_once("::").ok_or_else(|| {
|
||||
ProbeError::Args(format!(
|
||||
"invalid --step value (expected <label>::<payload>): {value}"
|
||||
))
|
||||
steps.push(parse_step_value(value, "--step")?);
|
||||
}
|
||||
"--step-file" => {
|
||||
index += 1;
|
||||
let value = args.get(index).ok_or_else(|| {
|
||||
ProbeError::Args("missing value for --step-file".to_string())
|
||||
})?;
|
||||
if label.is_empty() {
|
||||
return Err(ProbeError::Args("step label must not be empty".to_string()));
|
||||
let contents = fs::read_to_string(value).map_err(|err| {
|
||||
ProbeError::Args(format!("failed to read --step-file {value}: {err}"))
|
||||
})?;
|
||||
let normalized = contents.trim();
|
||||
if normalized.is_empty() {
|
||||
return Err(ProbeError::Args(format!(
|
||||
"--step-file must not be empty: {value}"
|
||||
)));
|
||||
}
|
||||
if payload.is_empty() {
|
||||
return Err(ProbeError::Args(
|
||||
"step payload must not be empty".to_string(),
|
||||
));
|
||||
}
|
||||
steps.push(ProbeStep {
|
||||
label: label.to_string(),
|
||||
payload: payload.to_string(),
|
||||
expect_reply: true,
|
||||
});
|
||||
steps.push(parse_step_value(normalized, "--step-file")?);
|
||||
}
|
||||
flag => {
|
||||
return Err(ProbeError::Args(format!("unknown argument: {flag}")));
|
||||
@@ -127,6 +127,27 @@ pub fn parse_probe_args(args: &[String]) -> Result<ProbeCliConfig, ProbeError> {
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_step_value(value: &str, source: &str) -> Result<ProbeStep, ProbeError> {
|
||||
let (label, payload) = value.split_once("::").ok_or_else(|| {
|
||||
ProbeError::Args(format!(
|
||||
"invalid {source} value (expected <label>::<payload>): {value}"
|
||||
))
|
||||
})?;
|
||||
if label.is_empty() {
|
||||
return Err(ProbeError::Args("step label must not be empty".to_string()));
|
||||
}
|
||||
if payload.is_empty() {
|
||||
return Err(ProbeError::Args(
|
||||
"step payload must not be empty".to_string(),
|
||||
));
|
||||
}
|
||||
Ok(ProbeStep {
|
||||
label: label.to_string(),
|
||||
payload: payload.to_string(),
|
||||
expect_reply: true,
|
||||
})
|
||||
}
|
||||
|
||||
fn validate_ws_url(ws_url: &str) -> Result<(), ProbeError> {
|
||||
if ws_url.starts_with("ws://") {
|
||||
return Ok(());
|
||||
|
||||
@@ -12,6 +12,18 @@ use zeroclaw::tools::{Tool, ToolResult};
|
||||
use crate::browser::BrowserBackend;
|
||||
use crate::pipe::Action;
|
||||
|
||||
fn preview_utf8(text: &str, max_chars: usize) -> &str {
|
||||
if text.chars().count() <= max_chars {
|
||||
return text;
|
||||
}
|
||||
|
||||
let mut end = 0usize;
|
||||
for (idx, ch) in text.char_indices().take(max_chars) {
|
||||
end = idx + ch.len_utf8();
|
||||
}
|
||||
&text[..end]
|
||||
}
|
||||
|
||||
pub struct BrowserScriptSkillTool {
|
||||
tool_name: String,
|
||||
tool_description: String,
|
||||
@@ -269,11 +281,7 @@ fn execute_browser_script_impl(
|
||||
);
|
||||
eprintln!(
|
||||
"[execute_browser_script_impl] 包装后脚本前500字符: {}",
|
||||
if wrapped_script.len() > 500 {
|
||||
&wrapped_script[..500]
|
||||
} else {
|
||||
&wrapped_script
|
||||
}
|
||||
preview_utf8(&wrapped_script, 500)
|
||||
);
|
||||
eprintln!("[execute_browser_script_impl] 调用 browser_tool.invoke(Action::Eval)...");
|
||||
|
||||
|
||||
@@ -165,13 +165,23 @@ fn build_scene_execution_plan(
|
||||
instruction: &str,
|
||||
mut args: Map<String, Value>,
|
||||
) -> SceneExecutionPlan {
|
||||
let bootstrap = entry
|
||||
.manifest
|
||||
.bootstrap
|
||||
.as_ref()
|
||||
.expect("report scene registry should only contain manifests with bootstrap");
|
||||
let artifact = entry
|
||||
.manifest
|
||||
.artifact
|
||||
.as_ref()
|
||||
.expect("report scene registry should only contain manifests with artifact");
|
||||
args.insert(
|
||||
"expected_domain".to_string(),
|
||||
Value::String(entry.manifest.bootstrap.expected_domain.clone()),
|
||||
Value::String(bootstrap.expected_domain.clone()),
|
||||
);
|
||||
args.insert(
|
||||
"target_url".to_string(),
|
||||
Value::String(entry.manifest.bootstrap.target_url.clone()),
|
||||
Value::String(bootstrap.target_url.clone()),
|
||||
);
|
||||
|
||||
SceneExecutionPlan {
|
||||
@@ -181,11 +191,11 @@ fn build_scene_execution_plan(
|
||||
"{}.{}",
|
||||
entry.manifest.scene.skill, entry.manifest.scene.tool
|
||||
),
|
||||
expected_domain: entry.manifest.bootstrap.expected_domain.clone(),
|
||||
target_url: entry.manifest.bootstrap.target_url.clone(),
|
||||
expected_domain: bootstrap.expected_domain.clone(),
|
||||
target_url: bootstrap.target_url.clone(),
|
||||
args,
|
||||
success_statuses: entry.manifest.artifact.success_status.clone(),
|
||||
failure_statuses: entry.manifest.artifact.failure_status.clone(),
|
||||
success_statuses: artifact.success_status.clone(),
|
||||
failure_statuses: artifact.failure_status.clone(),
|
||||
postprocess: entry.manifest.postprocess.clone(),
|
||||
}
|
||||
}
|
||||
@@ -252,7 +262,8 @@ fn score_scene(
|
||||
page_url: Option<&str>,
|
||||
page_title: Option<&str>,
|
||||
) -> Option<usize> {
|
||||
let deterministic = &entry.manifest.deterministic;
|
||||
let deterministic = entry.manifest.deterministic.as_ref()?;
|
||||
let bootstrap = entry.manifest.bootstrap.as_ref()?;
|
||||
if deterministic.suffix != DETERMINISTIC_SUFFIX {
|
||||
return None;
|
||||
}
|
||||
@@ -279,11 +290,7 @@ fn score_scene(
|
||||
let normalized_url = page_url.unwrap_or_default().to_ascii_lowercase();
|
||||
if !normalized_url.is_empty() {
|
||||
if normalized_url.contains(
|
||||
&entry
|
||||
.manifest
|
||||
.bootstrap
|
||||
.expected_domain
|
||||
.to_ascii_lowercase(),
|
||||
&bootstrap.expected_domain.to_ascii_lowercase(),
|
||||
) {
|
||||
score += 100;
|
||||
} else if normalized_url.contains(&entry.manifest.scene.id.to_ascii_lowercase()) {
|
||||
@@ -296,6 +303,7 @@ fn score_scene(
|
||||
&& entry
|
||||
.manifest
|
||||
.bootstrap
|
||||
.as_ref()?
|
||||
.page_title_keywords
|
||||
.iter()
|
||||
.any(|keyword| !keyword.trim().is_empty() && title.contains(keyword.as_str()))
|
||||
@@ -324,8 +332,18 @@ fn log_registry_diag(registry: &[SceneRegistryEntry]) {
|
||||
registry.len(),
|
||||
DIAGNOSTIC_SCENE_ID,
|
||||
entry.skill_root.display(),
|
||||
entry.manifest.deterministic.suffix == DETERMINISTIC_SUFFIX,
|
||||
entry.manifest.deterministic.include_keywords
|
||||
entry
|
||||
.manifest
|
||||
.deterministic
|
||||
.as_ref()
|
||||
.map(|deterministic| deterministic.suffix == DETERMINISTIC_SUFFIX)
|
||||
.unwrap_or(false),
|
||||
entry
|
||||
.manifest
|
||||
.deterministic
|
||||
.as_ref()
|
||||
.map(|deterministic| deterministic.include_keywords.clone())
|
||||
.unwrap_or_default()
|
||||
)),
|
||||
None => log_deterministic_diag(format!(
|
||||
"registry loaded count={} diagnostic_scene={} registered=false",
|
||||
@@ -336,7 +354,13 @@ fn log_registry_diag(registry: &[SceneRegistryEntry]) {
|
||||
}
|
||||
|
||||
fn log_scene_match_diag(entry: &SceneRegistryEntry, instruction: &str) {
|
||||
let deterministic = &entry.manifest.deterministic;
|
||||
let Some(deterministic) = entry.manifest.deterministic.as_ref() else {
|
||||
log_deterministic_diag(format!(
|
||||
"diagnostic_scene={} deterministic=false",
|
||||
entry.manifest.scene.id
|
||||
));
|
||||
return;
|
||||
};
|
||||
let include_hits = deterministic
|
||||
.include_keywords
|
||||
.iter()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod dispatch;
|
||||
pub mod registry;
|
||||
pub mod resolvers;
|
||||
pub mod scheduled_registry;
|
||||
|
||||
293
src/compat/scene_platform/scheduled_registry.rs
Normal file
293
src/compat/scene_platform/scheduled_registry.rs
Normal file
@@ -0,0 +1,293 @@
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use thiserror::Error;
|
||||
use zeroclaw::skills::{load_skills_from_directory, Skill};
|
||||
|
||||
use crate::scene_contract::manifest::{
|
||||
SceneManifest, SCENE_MANIFEST_FILE_NAME, SUPPORTED_SCHEMA_VERSION_V1,
|
||||
SUPPORTED_SCHEDULED_MONITORING_CATEGORY_V1, SUPPORTED_SCHEDULED_MONITORING_KIND_V1,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ScheduledMonitoringRegistryEntry {
|
||||
pub manifest: SceneManifest,
|
||||
pub skill_root: PathBuf,
|
||||
pub workflow_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ScheduledMonitoringRegistryError {
|
||||
#[error("failed to read skills directory {path}: {source}")]
|
||||
ReadSkillsDir {
|
||||
path: PathBuf,
|
||||
#[source]
|
||||
source: std::io::Error,
|
||||
},
|
||||
#[error("failed to read scene manifest {path}: {source}")]
|
||||
ReadManifest {
|
||||
path: PathBuf,
|
||||
#[source]
|
||||
source: std::io::Error,
|
||||
},
|
||||
#[error("failed to parse scene manifest {path}: {source}")]
|
||||
ParseManifest {
|
||||
path: PathBuf,
|
||||
#[source]
|
||||
source: toml::de::Error,
|
||||
},
|
||||
#[error("scheduled scene manifest {path} declares unsupported schema_version {version}; only {supported} is supported in v1")]
|
||||
UnsupportedSchemaVersion {
|
||||
path: PathBuf,
|
||||
version: String,
|
||||
supported: &'static str,
|
||||
},
|
||||
#[error("scheduled scene manifest {path} declares unsupported kind {kind}; only {supported} is supported")]
|
||||
UnsupportedSceneKind {
|
||||
path: PathBuf,
|
||||
kind: String,
|
||||
supported: &'static str,
|
||||
},
|
||||
#[error("scheduled scene manifest {path} declares unsupported category {category}; only {supported} is supported")]
|
||||
UnsupportedSceneCategory {
|
||||
path: PathBuf,
|
||||
category: String,
|
||||
supported: &'static str,
|
||||
},
|
||||
#[error("scheduled scene manifest {path} points to missing skill package {skill}")]
|
||||
MissingSkill { path: PathBuf, skill: String },
|
||||
#[error("scheduled scene manifest {path} declares skill {manifest_skill}, but containing skill package is {package_skill}")]
|
||||
SkillPackageMismatch {
|
||||
path: PathBuf,
|
||||
manifest_skill: String,
|
||||
package_skill: String,
|
||||
},
|
||||
#[error("scheduled scene manifest {path} is missing trigger section")]
|
||||
MissingTriggerSection { path: PathBuf },
|
||||
#[error("scheduled scene manifest {path} is missing modes section")]
|
||||
MissingModesSection { path: PathBuf },
|
||||
#[error("scheduled scene manifest {path} is missing runtime_context section")]
|
||||
MissingRuntimeContextSection { path: PathBuf },
|
||||
#[error("scheduled scene manifest {path} is missing safety section")]
|
||||
MissingSafetySection { path: PathBuf },
|
||||
#[error("scheduled scene manifest {path} is missing tools section")]
|
||||
MissingToolsSection { path: PathBuf },
|
||||
#[error("scheduled scene manifest {path} is missing references section")]
|
||||
MissingReferencesSection { path: PathBuf },
|
||||
#[error("scheduled scene manifest {path} is missing workflow_id")]
|
||||
MissingWorkflowId { path: PathBuf },
|
||||
#[error("scheduled scene manifest {path} has unsafe active or queue_process enablement")]
|
||||
UnsafeModesEnabled { path: PathBuf },
|
||||
#[error("scheduled scene manifest {path} incorrectly exposes natural-language primary trigger")]
|
||||
NaturalLanguagePrimaryEnabled { path: PathBuf },
|
||||
#[error("scheduled workflow id {workflow_id} is declared twice: {first_path} and {second_path}")]
|
||||
DuplicateWorkflowId {
|
||||
workflow_id: String,
|
||||
first_path: PathBuf,
|
||||
second_path: PathBuf,
|
||||
},
|
||||
}
|
||||
|
||||
pub fn load_scheduled_monitoring_registry(
|
||||
skills_dir: &Path,
|
||||
) -> Result<Vec<ScheduledMonitoringRegistryEntry>, ScheduledMonitoringRegistryError> {
|
||||
if !skills_dir.exists() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
let mut skill_roots = Vec::new();
|
||||
for entry in fs::read_dir(skills_dir).map_err(|source| {
|
||||
ScheduledMonitoringRegistryError::ReadSkillsDir {
|
||||
path: skills_dir.to_path_buf(),
|
||||
source,
|
||||
}
|
||||
})? {
|
||||
let entry = entry.map_err(|source| ScheduledMonitoringRegistryError::ReadSkillsDir {
|
||||
path: skills_dir.to_path_buf(),
|
||||
source,
|
||||
})?;
|
||||
let path = entry.path();
|
||||
if path.is_dir() {
|
||||
skill_roots.push(path);
|
||||
}
|
||||
}
|
||||
skill_roots.sort();
|
||||
|
||||
let skills_by_root = index_skills_by_root(skills_dir);
|
||||
let mut workflow_ids = HashMap::new();
|
||||
let mut registry = Vec::new();
|
||||
|
||||
for skill_root in skill_roots {
|
||||
let manifest_path = skill_root.join(SCENE_MANIFEST_FILE_NAME);
|
||||
if !manifest_path.exists() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let manifest = load_manifest(&manifest_path)?;
|
||||
if manifest.scene.kind != SUPPORTED_SCHEDULED_MONITORING_KIND_V1 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let workflow_id = validate_manifest(&manifest, &manifest_path, &skill_root, &skills_by_root)?;
|
||||
if let Some(first_path) = workflow_ids.insert(workflow_id.clone(), manifest_path.clone()) {
|
||||
return Err(ScheduledMonitoringRegistryError::DuplicateWorkflowId {
|
||||
workflow_id,
|
||||
first_path,
|
||||
second_path: manifest_path,
|
||||
});
|
||||
}
|
||||
|
||||
registry.push(ScheduledMonitoringRegistryEntry {
|
||||
manifest,
|
||||
skill_root,
|
||||
workflow_id,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(registry)
|
||||
}
|
||||
|
||||
fn index_skills_by_root(skills_dir: &Path) -> HashMap<PathBuf, Skill> {
|
||||
load_skills_from_directory(skills_dir, true)
|
||||
.into_iter()
|
||||
.filter_map(|skill| {
|
||||
let skill_root = skill
|
||||
.location
|
||||
.as_deref()
|
||||
.and_then(Path::parent)
|
||||
.map(Path::to_path_buf)?;
|
||||
Some((skill_root, skill))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn load_manifest(path: &Path) -> Result<SceneManifest, ScheduledMonitoringRegistryError> {
|
||||
let content =
|
||||
fs::read_to_string(path).map_err(|source| ScheduledMonitoringRegistryError::ReadManifest {
|
||||
path: path.to_path_buf(),
|
||||
source,
|
||||
})?;
|
||||
toml::from_str(&content).map_err(|source| ScheduledMonitoringRegistryError::ParseManifest {
|
||||
path: path.to_path_buf(),
|
||||
source,
|
||||
})
|
||||
}
|
||||
|
||||
fn validate_manifest(
|
||||
manifest: &SceneManifest,
|
||||
manifest_path: &Path,
|
||||
skill_root: &Path,
|
||||
skills_by_root: &HashMap<PathBuf, Skill>,
|
||||
) -> Result<String, ScheduledMonitoringRegistryError> {
|
||||
if manifest.manifest.schema_version != SUPPORTED_SCHEMA_VERSION_V1 {
|
||||
return Err(ScheduledMonitoringRegistryError::UnsupportedSchemaVersion {
|
||||
path: manifest_path.to_path_buf(),
|
||||
version: manifest.manifest.schema_version.clone(),
|
||||
supported: SUPPORTED_SCHEMA_VERSION_V1,
|
||||
});
|
||||
}
|
||||
|
||||
if manifest.scene.kind != SUPPORTED_SCHEDULED_MONITORING_KIND_V1 {
|
||||
return Err(ScheduledMonitoringRegistryError::UnsupportedSceneKind {
|
||||
path: manifest_path.to_path_buf(),
|
||||
kind: manifest.scene.kind.clone(),
|
||||
supported: SUPPORTED_SCHEDULED_MONITORING_KIND_V1,
|
||||
});
|
||||
}
|
||||
|
||||
if manifest.scene.category != SUPPORTED_SCHEDULED_MONITORING_CATEGORY_V1 {
|
||||
return Err(ScheduledMonitoringRegistryError::UnsupportedSceneCategory {
|
||||
path: manifest_path.to_path_buf(),
|
||||
category: manifest.scene.category.clone(),
|
||||
supported: SUPPORTED_SCHEDULED_MONITORING_CATEGORY_V1,
|
||||
});
|
||||
}
|
||||
|
||||
let trigger = manifest
|
||||
.trigger
|
||||
.as_ref()
|
||||
.ok_or_else(|| ScheduledMonitoringRegistryError::MissingTriggerSection {
|
||||
path: manifest_path.to_path_buf(),
|
||||
})?;
|
||||
let modes = manifest
|
||||
.modes
|
||||
.as_ref()
|
||||
.ok_or_else(|| ScheduledMonitoringRegistryError::MissingModesSection {
|
||||
path: manifest_path.to_path_buf(),
|
||||
})?;
|
||||
let _runtime_context = manifest.runtime_context.as_ref().ok_or_else(|| {
|
||||
ScheduledMonitoringRegistryError::MissingRuntimeContextSection {
|
||||
path: manifest_path.to_path_buf(),
|
||||
}
|
||||
})?;
|
||||
let safety = manifest
|
||||
.safety
|
||||
.as_ref()
|
||||
.ok_or_else(|| ScheduledMonitoringRegistryError::MissingSafetySection {
|
||||
path: manifest_path.to_path_buf(),
|
||||
})?;
|
||||
let tools = manifest
|
||||
.tools
|
||||
.as_ref()
|
||||
.ok_or_else(|| ScheduledMonitoringRegistryError::MissingToolsSection {
|
||||
path: manifest_path.to_path_buf(),
|
||||
})?;
|
||||
let _references = manifest.references.as_ref().ok_or_else(|| {
|
||||
ScheduledMonitoringRegistryError::MissingReferencesSection {
|
||||
path: manifest_path.to_path_buf(),
|
||||
}
|
||||
})?;
|
||||
|
||||
let workflow_id = manifest
|
||||
.scene
|
||||
.workflow_id
|
||||
.clone()
|
||||
.filter(|value| !value.trim().is_empty())
|
||||
.ok_or_else(|| ScheduledMonitoringRegistryError::MissingWorkflowId {
|
||||
path: manifest_path.to_path_buf(),
|
||||
})?;
|
||||
|
||||
if safety.active_enabled || safety.queue_process_enabled {
|
||||
return Err(ScheduledMonitoringRegistryError::UnsafeModesEnabled {
|
||||
path: manifest_path.to_path_buf(),
|
||||
});
|
||||
}
|
||||
|
||||
if trigger.natural_language_primary {
|
||||
return Err(ScheduledMonitoringRegistryError::NaturalLanguagePrimaryEnabled {
|
||||
path: manifest_path.to_path_buf(),
|
||||
});
|
||||
}
|
||||
|
||||
if !modes.enabled.iter().any(|mode| mode == "dry_run" || mode == "monitor_only") {
|
||||
return Err(ScheduledMonitoringRegistryError::UnsafeModesEnabled {
|
||||
path: manifest_path.to_path_buf(),
|
||||
});
|
||||
}
|
||||
|
||||
let Some(skill) = skills_by_root.get(skill_root) else {
|
||||
return Err(ScheduledMonitoringRegistryError::MissingSkill {
|
||||
path: manifest_path.to_path_buf(),
|
||||
skill: manifest.scene.skill.clone(),
|
||||
});
|
||||
};
|
||||
|
||||
if skill.name != manifest.scene.skill {
|
||||
return Err(ScheduledMonitoringRegistryError::SkillPackageMismatch {
|
||||
path: manifest_path.to_path_buf(),
|
||||
manifest_skill: manifest.scene.skill.clone(),
|
||||
package_skill: skill.name.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
for expected_tool in [&tools.detect, &tools.decide, &tools.action_plan] {
|
||||
if expected_tool.trim().is_empty() {
|
||||
return Err(ScheduledMonitoringRegistryError::MissingToolsSection {
|
||||
path: manifest_path.to_path_buf(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(workflow_id)
|
||||
}
|
||||
@@ -152,6 +152,8 @@ pub struct SgClawSettings {
|
||||
pub office_backend: OfficeBackend,
|
||||
pub browser_ws_url: Option<String>,
|
||||
pub service_ws_listen_addr: Option<String>,
|
||||
pub scheduled_monitoring_platform_service_base_url: Option<String>,
|
||||
pub scheduled_monitoring_watch_tasks: Vec<String>,
|
||||
}
|
||||
|
||||
impl SgClawSettings {
|
||||
@@ -190,6 +192,8 @@ impl SgClawSettings {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Vec::new(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -237,6 +241,16 @@ impl SgClawSettings {
|
||||
}),
|
||||
browser_ws_url: self.browser_ws_url.clone(),
|
||||
service_ws_listen_addr: self.service_ws_listen_addr.clone(),
|
||||
scheduled_monitoring_platform_service_base_url: self
|
||||
.scheduled_monitoring_platform_service_base_url
|
||||
.clone(),
|
||||
scheduled_monitoring: if self.scheduled_monitoring_watch_tasks.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(SerializableScheduledMonitoringSettings {
|
||||
watch_tasks: self.scheduled_monitoring_watch_tasks.clone(),
|
||||
})
|
||||
},
|
||||
providers: self
|
||||
.providers
|
||||
.iter()
|
||||
@@ -290,6 +304,8 @@ impl SgClawSettings {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Vec::new(),
|
||||
)?))
|
||||
}
|
||||
|
||||
@@ -376,6 +392,11 @@ impl SgClawSettings {
|
||||
office_backend,
|
||||
config.browser_ws_url,
|
||||
config.service_ws_listen_addr,
|
||||
config.scheduled_monitoring_platform_service_base_url,
|
||||
config
|
||||
.scheduled_monitoring
|
||||
.map(|value| value.watch_tasks)
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
.map_err(|err| err.with_path(path))
|
||||
}
|
||||
@@ -395,6 +416,8 @@ impl SgClawSettings {
|
||||
office_backend: Option<OfficeBackend>,
|
||||
browser_ws_url: Option<String>,
|
||||
service_ws_listen_addr: Option<String>,
|
||||
scheduled_monitoring_platform_service_base_url: Option<String>,
|
||||
scheduled_monitoring_watch_tasks: Vec<String>,
|
||||
) -> Result<Self, ConfigError> {
|
||||
let direct_submit_skill = normalize_direct_submit_skill(direct_submit_skill)?;
|
||||
let providers = if providers.is_empty() {
|
||||
@@ -438,6 +461,10 @@ impl SgClawSettings {
|
||||
office_backend: office_backend.unwrap_or(OfficeBackend::OpenXml),
|
||||
browser_ws_url: normalize_optional_value(browser_ws_url),
|
||||
service_ws_listen_addr: normalize_optional_value(service_ws_listen_addr),
|
||||
scheduled_monitoring_platform_service_base_url: normalize_optional_value(
|
||||
scheduled_monitoring_platform_service_base_url,
|
||||
),
|
||||
scheduled_monitoring_watch_tasks,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -627,10 +654,26 @@ struct SerializableRawSgClawSettings {
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
service_ws_listen_addr: Option<String>,
|
||||
#[serde(
|
||||
rename = "scheduledMonitoringPlatformServiceBaseUrl",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
scheduled_monitoring_platform_service_base_url: Option<String>,
|
||||
#[serde(
|
||||
rename = "scheduledMonitoring",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
scheduled_monitoring: Option<SerializableScheduledMonitoringSettings>,
|
||||
#[serde(default)]
|
||||
providers: Vec<SerializableProviderSettings>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct SerializableScheduledMonitoringSettings {
|
||||
#[serde(rename = "watchTasks")]
|
||||
watch_tasks: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct SerializableProviderSettings {
|
||||
id: String,
|
||||
@@ -680,10 +723,24 @@ struct RawSgClawSettings {
|
||||
default
|
||||
)]
|
||||
service_ws_listen_addr: Option<String>,
|
||||
#[serde(
|
||||
rename = "scheduledMonitoringPlatformServiceBaseUrl",
|
||||
alias = "scheduled_monitoring_platform_service_base_url",
|
||||
default
|
||||
)]
|
||||
scheduled_monitoring_platform_service_base_url: Option<String>,
|
||||
#[serde(rename = "scheduledMonitoring", default)]
|
||||
scheduled_monitoring: Option<RawScheduledMonitoringSettings>,
|
||||
#[serde(default)]
|
||||
providers: Vec<RawProviderSettings>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct RawScheduledMonitoringSettings {
|
||||
#[serde(rename = "watchTasks", alias = "watch_tasks", default)]
|
||||
watch_tasks: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct RawProviderSettings {
|
||||
#[serde(default)]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,8 @@ pub enum WorkflowArchetype {
|
||||
LocalDocPipeline,
|
||||
#[serde(rename = "page_state_eval")]
|
||||
PageStateEval,
|
||||
#[serde(rename = "monitoring_action_workflow")]
|
||||
MonitoringActionWorkflow,
|
||||
}
|
||||
|
||||
impl WorkflowArchetype {
|
||||
@@ -34,6 +36,7 @@ impl WorkflowArchetype {
|
||||
Self::MultiEndpointInventory => "multi_endpoint_inventory",
|
||||
Self::LocalDocPipeline => "local_doc_pipeline",
|
||||
Self::PageStateEval => "page_state_eval",
|
||||
Self::MonitoringActionWorkflow => "monitoring_action_workflow",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +50,7 @@ impl WorkflowArchetype {
|
||||
"multi_endpoint_inventory" => Some(Self::MultiEndpointInventory),
|
||||
"local_doc_pipeline" => Some(Self::LocalDocPipeline),
|
||||
"page_state_eval" => Some(Self::PageStateEval),
|
||||
"monitoring_action_workflow" => Some(Self::MonitoringActionWorkflow),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -252,6 +256,259 @@ pub struct RuntimeDependencyIr {
|
||||
pub subordinate_to_business_chain: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct MonitoringRuntimeContextIr {
|
||||
#[serde(rename = "runtimeContextUrl", default)]
|
||||
pub runtime_context_url: String,
|
||||
#[serde(rename = "expectedDomain", default)]
|
||||
pub expected_domain: String,
|
||||
#[serde(rename = "gatewayDomain", default)]
|
||||
pub gateway_domain: String,
|
||||
#[serde(rename = "localhostServiceBase", default)]
|
||||
pub localhost_service_base: String,
|
||||
#[serde(rename = "browserAttachedRequired", default)]
|
||||
pub browser_attached_required: bool,
|
||||
#[serde(rename = "hostBridgeRequired", default)]
|
||||
pub host_bridge_required: bool,
|
||||
#[serde(
|
||||
rename = "executionContextMode",
|
||||
default = "default_execution_context_mode"
|
||||
)]
|
||||
pub execution_context_mode: String,
|
||||
#[serde(
|
||||
rename = "requestClientMode",
|
||||
default = "default_request_client_mode"
|
||||
)]
|
||||
pub request_client_mode: String,
|
||||
#[serde(rename = "encryptionMode", default = "default_encryption_mode")]
|
||||
pub encryption_mode: String,
|
||||
#[serde(
|
||||
rename = "attachedPageBrowserActionPolicy",
|
||||
default = "default_attached_page_browser_action_policy"
|
||||
)]
|
||||
pub attached_page_browser_action_policy: String,
|
||||
#[serde(
|
||||
rename = "platformWritePolicy",
|
||||
default = "default_platform_write_policy"
|
||||
)]
|
||||
pub platform_write_policy: String,
|
||||
#[serde(rename = "storageReads", default)]
|
||||
pub storage_reads: Vec<MonitoringStorageReadIr>,
|
||||
#[serde(rename = "readSlices", default)]
|
||||
pub read_slices: Vec<MonitoringReadSliceIr>,
|
||||
#[serde(rename = "encryptionResolution", default)]
|
||||
pub encryption_resolution: MonitoringEncryptionResolutionIr,
|
||||
#[serde(rename = "timeoutContract", default)]
|
||||
pub timeout_contract: MonitoringTimeoutContractIr,
|
||||
#[serde(rename = "outputContract", default)]
|
||||
pub output_contract: MonitoringOutputContractIr,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct MonitoringStorageReadIr {
|
||||
#[serde(default)]
|
||||
pub key: String,
|
||||
#[serde(default)]
|
||||
pub source: String,
|
||||
#[serde(rename = "fallbackOrder", default)]
|
||||
pub fallback_order: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub required: bool,
|
||||
#[serde(rename = "parseMode", default)]
|
||||
pub parse_mode: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct MonitoringReadSliceIr {
|
||||
#[serde(default)]
|
||||
pub name: String,
|
||||
#[serde(rename = "endpointBinding", default)]
|
||||
pub endpoint_binding: String,
|
||||
#[serde(rename = "requestTemplateOverride", default)]
|
||||
pub request_template_override: Value,
|
||||
#[serde(rename = "responsePath", default)]
|
||||
pub response_path: String,
|
||||
#[serde(rename = "timeoutMs", default)]
|
||||
pub timeout_ms: u64,
|
||||
#[serde(rename = "mergeRole", default)]
|
||||
pub merge_role: String,
|
||||
#[serde(default)]
|
||||
pub required: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct MonitoringEncryptionResolutionIr {
|
||||
#[serde(rename = "primaryMethod", default)]
|
||||
pub primary_method: String,
|
||||
#[serde(rename = "fallbackMethods", default)]
|
||||
pub fallback_methods: Vec<String>,
|
||||
#[serde(rename = "requiredContext", default)]
|
||||
pub required_context: Vec<String>,
|
||||
#[serde(rename = "hardFail", default)]
|
||||
pub hard_fail: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct MonitoringTimeoutContractIr {
|
||||
#[serde(rename = "perStepTimeoutMs", default)]
|
||||
pub per_step_timeout_ms: u64,
|
||||
#[serde(rename = "overallDetectTimeoutMs", default)]
|
||||
pub overall_detect_timeout_ms: u64,
|
||||
#[serde(rename = "statusOnTimeout", default)]
|
||||
pub status_on_timeout: String,
|
||||
#[serde(rename = "statusOnPartial", default)]
|
||||
pub status_on_partial: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct MonitoringSidecarOutputIr {
|
||||
#[serde(default)]
|
||||
pub name: String,
|
||||
#[serde(rename = "relativePath", default)]
|
||||
pub relative_path: String,
|
||||
#[serde(rename = "payloadSchema", default)]
|
||||
pub payload_schema: Value,
|
||||
#[serde(rename = "sourceField", default)]
|
||||
pub source_field: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct MonitoringOutputContractIr {
|
||||
#[serde(rename = "runRecordMode", default = "default_run_record_mode")]
|
||||
pub run_record_mode: String,
|
||||
#[serde(rename = "businessLogEnabled", default = "default_true")]
|
||||
pub business_log_enabled: bool,
|
||||
#[serde(rename = "sidecarOutputs", default)]
|
||||
pub sidecar_outputs: Vec<MonitoringSidecarOutputIr>,
|
||||
#[serde(rename = "deltaState", default)]
|
||||
pub delta_state: Option<MonitoringDeltaStateIr>,
|
||||
}
|
||||
|
||||
impl Default for MonitoringOutputContractIr {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
run_record_mode: default_run_record_mode(),
|
||||
business_log_enabled: true,
|
||||
sidecar_outputs: Vec::new(),
|
||||
delta_state: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct MonitoringDeltaStateIr {
|
||||
#[serde(rename = "identityFields", default)]
|
||||
pub identity_fields: Vec<String>,
|
||||
#[serde(rename = "stateSidecarPath", default)]
|
||||
pub state_sidecar_path: String,
|
||||
#[serde(rename = "comparisonMode", default)]
|
||||
pub comparison_mode: String,
|
||||
#[serde(rename = "emitPolicy", default)]
|
||||
pub emit_policy: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct MonitoringDependencyIr {
|
||||
#[serde(default)]
|
||||
pub name: String,
|
||||
#[serde(default)]
|
||||
pub url: String,
|
||||
#[serde(default)]
|
||||
pub classification: String,
|
||||
#[serde(rename = "sideEffect", default)]
|
||||
pub side_effect: bool,
|
||||
#[serde(rename = "blockedByDefault", default)]
|
||||
pub blocked_by_default: bool,
|
||||
}
|
||||
|
||||
impl MonitoringDependencyIr {
|
||||
pub fn normalized_classification(&self) -> String {
|
||||
let url = self.url.trim().to_ascii_lowercase();
|
||||
if url.starts_with("http://localhost:13313/")
|
||||
|| url.starts_with("https://localhost:13313/")
|
||||
{
|
||||
return "host_runtime_local_service".to_string();
|
||||
}
|
||||
if url.contains("/monitorservices/")
|
||||
|| url.contains("/marketingservices/")
|
||||
|| url.contains("/configservices/")
|
||||
|| url.contains("/reportservices/")
|
||||
|| url.contains("/surfaceservices/")
|
||||
|| url.contains("/msgoffilecenter/")
|
||||
{
|
||||
return "remote_platform_service".to_string();
|
||||
}
|
||||
let classification = self.classification.trim();
|
||||
if classification.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
classification.to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct MonitoringSideEffectIr {
|
||||
#[serde(default)]
|
||||
pub action: String,
|
||||
#[serde(default)]
|
||||
pub signals: Vec<String>,
|
||||
#[serde(rename = "blockedByDefault", default)]
|
||||
pub blocked_by_default: bool,
|
||||
#[serde(rename = "requiredFutureGate", default)]
|
||||
pub required_future_gate: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct MonitoringSideEffectPolicyIr {
|
||||
#[serde(rename = "dryRunDefault", default)]
|
||||
pub dry_run_default: bool,
|
||||
#[serde(rename = "requiresExplicitConfirmation", default)]
|
||||
pub requires_explicit_confirmation: bool,
|
||||
#[serde(rename = "previewBeforeAction", default)]
|
||||
pub preview_before_action: bool,
|
||||
#[serde(rename = "maxItemsRequiredForActionModes", default)]
|
||||
pub max_items_required_for_action_modes: bool,
|
||||
#[serde(rename = "auditRecordRequired", default)]
|
||||
pub audit_record_required: bool,
|
||||
#[serde(rename = "blockedCallSignatures", default)]
|
||||
pub blocked_call_signatures: Vec<String>,
|
||||
#[serde(rename = "blockedActions", default)]
|
||||
pub blocked_actions: Vec<MonitoringSideEffectIr>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct MonitoringActionWorkflowIr {
|
||||
#[serde(rename = "workflowId", default)]
|
||||
pub workflow_id: String,
|
||||
#[serde(rename = "displayName", default)]
|
||||
pub display_name: String,
|
||||
#[serde(rename = "defaultMode", default)]
|
||||
pub default_mode: String,
|
||||
#[serde(rename = "workflowStages", default)]
|
||||
pub workflow_stages: Vec<String>,
|
||||
#[serde(rename = "mvpAllowedStages", default)]
|
||||
pub mvp_allowed_stages: Vec<String>,
|
||||
#[serde(rename = "blockedByDefaultStages", default)]
|
||||
pub blocked_by_default_stages: Vec<String>,
|
||||
#[serde(rename = "runtimeContext", default)]
|
||||
pub runtime_context: MonitoringRuntimeContextIr,
|
||||
#[serde(rename = "localStorageReads", default)]
|
||||
pub local_storage_reads: Vec<String>,
|
||||
#[serde(rename = "sessionStorageReads", default)]
|
||||
pub session_storage_reads: Vec<String>,
|
||||
#[serde(rename = "localServiceDependencies", default)]
|
||||
pub local_service_dependencies: Vec<MonitoringDependencyIr>,
|
||||
#[serde(rename = "businessApiDependencies", default)]
|
||||
pub business_api_dependencies: Vec<MonitoringDependencyIr>,
|
||||
#[serde(rename = "previewSchema", default)]
|
||||
pub preview_schema: Vec<String>,
|
||||
#[serde(rename = "sideEffectPolicy", default)]
|
||||
pub side_effect_policy: MonitoringSideEffectPolicyIr,
|
||||
#[serde(rename = "archetype", default = "default_monitoring_archetype")]
|
||||
pub archetype: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct MergeFieldMappingIr {
|
||||
#[serde(rename = "outputField", default)]
|
||||
@@ -296,6 +553,38 @@ impl Default for ArtifactContractIr {
|
||||
}
|
||||
}
|
||||
|
||||
fn default_execution_context_mode() -> String {
|
||||
"attached_page_direct".to_string()
|
||||
}
|
||||
|
||||
fn default_request_client_mode() -> String {
|
||||
"isolated_xhr".to_string()
|
||||
}
|
||||
|
||||
fn default_encryption_mode() -> String {
|
||||
"emsslib_data_encrypt_pub".to_string()
|
||||
}
|
||||
|
||||
fn default_attached_page_browser_action_policy() -> String {
|
||||
"forbid_secondary_jump".to_string()
|
||||
}
|
||||
|
||||
fn default_platform_write_policy() -> String {
|
||||
"skip_when_zero".to_string()
|
||||
}
|
||||
|
||||
fn default_run_record_mode() -> String {
|
||||
"per_scene_file".to_string()
|
||||
}
|
||||
|
||||
fn default_true() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn default_monitoring_archetype() -> String {
|
||||
"marketing_gateway_monitor".to_string()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct ValidationHintsIr {
|
||||
#[serde(
|
||||
@@ -447,6 +736,8 @@ pub struct SceneIr {
|
||||
pub confidence: f64,
|
||||
#[serde(default)]
|
||||
pub uncertainties: Vec<String>,
|
||||
#[serde(rename = "monitoringActionWorkflow", default)]
|
||||
pub monitoring_action_workflow: Option<MonitoringActionWorkflowIr>,
|
||||
}
|
||||
|
||||
impl SceneIr {
|
||||
@@ -620,6 +911,7 @@ impl From<LegacySceneInfoJson> for SceneIr {
|
||||
column_defs: value.column_defs,
|
||||
confidence: 0.0,
|
||||
uncertainties: Vec::new(),
|
||||
monitoring_action_workflow: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,3 +2,4 @@ pub mod analyzer;
|
||||
pub mod generator;
|
||||
pub mod ir;
|
||||
pub mod lessons;
|
||||
pub mod scheduled_monitoring_runtime;
|
||||
|
||||
2430
src/generated_scene/scheduled_monitoring_runtime.rs
Normal file
2430
src/generated_scene/scheduled_monitoring_runtime.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -5,28 +5,50 @@ pub const SCENE_MANIFEST_FILE_NAME: &str = "scene.toml";
|
||||
pub const SUPPORTED_SCHEMA_VERSION_V1: &str = "1";
|
||||
pub const SUPPORTED_SCENE_KIND_V1: &str = "browser_script";
|
||||
pub const SUPPORTED_SCENE_CATEGORY_V1: &str = "report_collection";
|
||||
pub const SUPPORTED_SCHEDULED_MONITORING_KIND_V1: &str = "scheduled_monitoring_action_workflow";
|
||||
pub const SUPPORTED_SCHEDULED_MONITORING_CATEGORY_V1: &str = "monitoring";
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SceneManifest {
|
||||
pub scene: SceneSection,
|
||||
pub manifest: ManifestSection,
|
||||
pub bootstrap: BootstrapSection,
|
||||
pub deterministic: DeterministicSection,
|
||||
#[serde(default)]
|
||||
pub bootstrap: Option<BootstrapSection>,
|
||||
#[serde(default)]
|
||||
pub deterministic: Option<DeterministicSection>,
|
||||
#[serde(default)]
|
||||
pub params: Vec<SceneParam>,
|
||||
pub artifact: ArtifactSection,
|
||||
#[serde(default)]
|
||||
pub artifact: Option<ArtifactSection>,
|
||||
#[serde(default)]
|
||||
pub postprocess: Option<PostprocessSection>,
|
||||
#[serde(default)]
|
||||
pub trigger: Option<TriggerSection>,
|
||||
#[serde(default)]
|
||||
pub modes: Option<ModesSection>,
|
||||
#[serde(default)]
|
||||
pub runtime_context: Option<RuntimeContextSection>,
|
||||
#[serde(default)]
|
||||
pub safety: Option<SafetySection>,
|
||||
#[serde(default)]
|
||||
pub tools: Option<ScheduledToolsSection>,
|
||||
#[serde(default)]
|
||||
pub references: Option<ScheduledReferencesSection>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SceneSection {
|
||||
pub id: String,
|
||||
pub skill: String,
|
||||
#[serde(default)]
|
||||
pub tool: String,
|
||||
pub kind: String,
|
||||
pub version: String,
|
||||
pub category: String,
|
||||
#[serde(default)]
|
||||
pub workflow_id: Option<String>,
|
||||
#[serde(default)]
|
||||
pub archetype: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -34,7 +56,7 @@ pub struct ManifestSection {
|
||||
pub schema_version: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct BootstrapSection {
|
||||
pub expected_domain: String,
|
||||
pub target_url: String,
|
||||
@@ -43,7 +65,7 @@ pub struct BootstrapSection {
|
||||
pub requires_target_page: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct DeterministicSection {
|
||||
pub suffix: String,
|
||||
#[serde(default)]
|
||||
@@ -63,7 +85,7 @@ pub struct SceneParam {
|
||||
pub resolver_config: Table,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct ArtifactSection {
|
||||
#[serde(rename = "type")]
|
||||
pub artifact_type: String,
|
||||
@@ -79,3 +101,201 @@ pub struct PostprocessSection {
|
||||
#[serde(default)]
|
||||
pub auto_open: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct TriggerSection {
|
||||
#[serde(default)]
|
||||
pub primary: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub validation: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub allowed: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub natural_language_primary: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct ModesSection {
|
||||
#[serde(default)]
|
||||
pub enabled: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub disabled: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub default: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct RuntimeContextSection {
|
||||
#[serde(default)]
|
||||
pub runtime_context_url: String,
|
||||
#[serde(default)]
|
||||
pub expected_domain: String,
|
||||
#[serde(default)]
|
||||
pub gateway_domain: String,
|
||||
#[serde(default)]
|
||||
pub localhost_service_base: String,
|
||||
#[serde(default)]
|
||||
pub browser_attached_required: bool,
|
||||
#[serde(default)]
|
||||
pub host_bridge_required: bool,
|
||||
#[serde(default)]
|
||||
pub execution_context_mode: String,
|
||||
#[serde(default)]
|
||||
pub request_client_mode: String,
|
||||
#[serde(default)]
|
||||
pub encryption_mode: String,
|
||||
#[serde(default)]
|
||||
pub attached_page_browser_action_policy: String,
|
||||
#[serde(default)]
|
||||
pub platform_write_policy: String,
|
||||
#[serde(default)]
|
||||
pub storage_reads: Vec<StorageReadSection>,
|
||||
#[serde(default)]
|
||||
pub read_slices: Vec<ReadSliceSection>,
|
||||
#[serde(default)]
|
||||
pub encryption_resolution: EncryptionResolutionSection,
|
||||
#[serde(default)]
|
||||
pub timeout_contract: TimeoutContractSection,
|
||||
#[serde(default)]
|
||||
pub output_contract: OutputContractSection,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct StorageReadSection {
|
||||
#[serde(default)]
|
||||
pub key: String,
|
||||
#[serde(default)]
|
||||
pub source: String,
|
||||
#[serde(default)]
|
||||
pub fallback_order: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub required: bool,
|
||||
#[serde(default)]
|
||||
pub parse_mode: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct ReadSliceSection {
|
||||
#[serde(default)]
|
||||
pub name: String,
|
||||
#[serde(default)]
|
||||
pub endpoint_binding: String,
|
||||
#[serde(default)]
|
||||
pub request_template_override: Option<serde_json::Value>,
|
||||
#[serde(default)]
|
||||
pub response_path: String,
|
||||
#[serde(default)]
|
||||
pub timeout_ms: u64,
|
||||
#[serde(default)]
|
||||
pub merge_role: String,
|
||||
#[serde(default)]
|
||||
pub required: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct EncryptionResolutionSection {
|
||||
#[serde(default)]
|
||||
pub primary_method: String,
|
||||
#[serde(default)]
|
||||
pub fallback_methods: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub required_context: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub hard_fail: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct TimeoutContractSection {
|
||||
#[serde(default)]
|
||||
pub per_step_timeout_ms: u64,
|
||||
#[serde(default)]
|
||||
pub overall_detect_timeout_ms: u64,
|
||||
#[serde(default)]
|
||||
pub status_on_timeout: String,
|
||||
#[serde(default)]
|
||||
pub status_on_partial: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct OutputContractSection {
|
||||
#[serde(default)]
|
||||
pub run_record_mode: String,
|
||||
#[serde(default)]
|
||||
pub business_log_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub sidecar_outputs: Vec<SidecarOutputSection>,
|
||||
#[serde(default)]
|
||||
pub delta_state: Option<DeltaStateSection>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct SidecarOutputSection {
|
||||
#[serde(default)]
|
||||
pub name: String,
|
||||
#[serde(default)]
|
||||
pub relative_path: String,
|
||||
#[serde(default)]
|
||||
pub source_field: String,
|
||||
#[serde(default)]
|
||||
pub payload_schema: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct DeltaStateSection {
|
||||
#[serde(default)]
|
||||
pub identity_fields: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub state_sidecar_path: String,
|
||||
#[serde(default)]
|
||||
pub comparison_mode: String,
|
||||
#[serde(default)]
|
||||
pub emit_policy: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct SafetySection {
|
||||
#[serde(default)]
|
||||
pub dry_run_default: bool,
|
||||
#[serde(default)]
|
||||
pub active_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub queue_process_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub side_effects_default: String,
|
||||
#[serde(default)]
|
||||
pub audit_required_before_active: bool,
|
||||
#[serde(default)]
|
||||
pub idempotency_required_before_active: bool,
|
||||
#[serde(default)]
|
||||
pub blocked_call_signatures: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct ScheduledToolsSection {
|
||||
#[serde(default)]
|
||||
pub detect: String,
|
||||
#[serde(default)]
|
||||
pub decide: String,
|
||||
#[serde(default)]
|
||||
pub action_plan: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct ScheduledReferencesSection {
|
||||
#[serde(default)]
|
||||
pub source_evidence: String,
|
||||
#[serde(default)]
|
||||
pub workflow_ir: String,
|
||||
#[serde(default)]
|
||||
pub trigger_contract: String,
|
||||
#[serde(default)]
|
||||
pub platform_dependencies: String,
|
||||
#[serde(default)]
|
||||
pub side_effect_policy: String,
|
||||
#[serde(default)]
|
||||
pub audit_policy: String,
|
||||
#[serde(default)]
|
||||
pub idempotency_policy: String,
|
||||
#[serde(default)]
|
||||
pub generation_report: String,
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
pub mod manifest;
|
||||
|
||||
pub use manifest::{
|
||||
ArtifactSection, BootstrapSection, DeterministicSection, ManifestSection, PostprocessSection,
|
||||
SceneManifest, SceneParam, SceneSection, SCENE_MANIFEST_FILE_NAME, SUPPORTED_SCENE_CATEGORY_V1,
|
||||
SUPPORTED_SCENE_KIND_V1, SUPPORTED_SCHEMA_VERSION_V1,
|
||||
ArtifactSection, BootstrapSection, DeterministicSection, ManifestSection, ModesSection,
|
||||
PostprocessSection, RuntimeContextSection, SafetySection, ScheduledReferencesSection,
|
||||
ScheduledToolsSection, SceneManifest, SceneParam, SceneSection, TriggerSection,
|
||||
SCENE_MANIFEST_FILE_NAME, SUPPORTED_SCENE_CATEGORY_V1, SUPPORTED_SCENE_KIND_V1,
|
||||
SUPPORTED_SCHEMA_VERSION_V1, SUPPORTED_SCHEDULED_MONITORING_CATEGORY_V1,
|
||||
SUPPORTED_SCHEDULED_MONITORING_KIND_V1,
|
||||
};
|
||||
|
||||
@@ -364,6 +364,7 @@ pub(crate) fn serve_client(
|
||||
Duration::from_secs(15),
|
||||
BROWSER_RESPONSE_TIMEOUT,
|
||||
true, // use_hidden_domain: hidden domain for invisible helper
|
||||
None,
|
||||
) {
|
||||
Ok(host) => {
|
||||
send_info_log(sink.as_ref(), "callback-host startup ready")?;
|
||||
|
||||
@@ -2,12 +2,14 @@ mod common;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::panic::AssertUnwindSafe;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use common::MockTransport;
|
||||
use futures_util::FutureExt;
|
||||
use serde_json::json;
|
||||
use sgclaw::browser::{BrowserBackend, PipeBrowserBackend};
|
||||
use sgclaw::compat::browser_script_skill_tool::{
|
||||
@@ -201,6 +203,77 @@ async fn execute_browser_script_tool_rejects_missing_expected_domain() {
|
||||
assert!(transport.sent_messages().is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn execute_browser_script_tool_handles_multibyte_wrapped_script_preview_without_panicking() {
|
||||
let skill_dir = unique_temp_dir("sgclaw-browser-script-helper-utf8-preview");
|
||||
let scripts_dir = skill_dir.join("scripts");
|
||||
fs::create_dir_all(&scripts_dir).unwrap();
|
||||
|
||||
let args_json = json!({
|
||||
"expected_domain": "www.zhihu.com"
|
||||
});
|
||||
let prefix_len = format!("(function() {{\nconst args = {};\n", args_json)
|
||||
.len();
|
||||
let comment_prefix = "//";
|
||||
let ascii_padding = (0..3)
|
||||
.find(|pad| (500usize - (prefix_len + comment_prefix.len() + *pad)) % 3 != 0)
|
||||
.expect("should find a padding value that forces byte 500 off a char boundary");
|
||||
let script_body = format!(
|
||||
"{}{}{}\nreturn {{ ok: true }};\n",
|
||||
comment_prefix,
|
||||
"a".repeat(ascii_padding),
|
||||
"归".repeat(220)
|
||||
);
|
||||
fs::write(scripts_dir.join("utf8_preview.js"), script_body).unwrap();
|
||||
|
||||
let transport = Arc::new(MockTransport::new(vec![BrowserMessage::Response {
|
||||
seq: 1,
|
||||
success: true,
|
||||
data: json!({
|
||||
"text": {
|
||||
"ok": true
|
||||
}
|
||||
}),
|
||||
aom_snapshot: vec![],
|
||||
timing: Timing {
|
||||
queue_ms: 1,
|
||||
exec_ms: 5,
|
||||
},
|
||||
}]));
|
||||
let browser_tool = BrowserPipeTool::new(
|
||||
transport.clone(),
|
||||
test_policy(),
|
||||
vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||
)
|
||||
.with_response_timeout(Duration::from_secs(1));
|
||||
|
||||
let skill_tool = SkillTool {
|
||||
name: "utf8_preview".to_string(),
|
||||
description: "Regression for UTF-8 safe wrapped script preview logging".to_string(),
|
||||
kind: "browser_script".to_string(),
|
||||
command: "scripts/utf8_preview.js".to_string(),
|
||||
args: HashMap::new(),
|
||||
};
|
||||
|
||||
let result = AssertUnwindSafe(execute_browser_script_tool(
|
||||
&skill_tool,
|
||||
&skill_dir,
|
||||
&PipeBrowserBackend::from_inner(browser_tool),
|
||||
json!({
|
||||
"expected_domain": "www.zhihu.com"
|
||||
}),
|
||||
))
|
||||
.catch_unwind()
|
||||
.await;
|
||||
|
||||
assert!(
|
||||
result.is_ok(),
|
||||
"wrapped script preview should not panic on multibyte UTF-8 content"
|
||||
);
|
||||
let tool_result = result.unwrap().unwrap();
|
||||
assert!(tool_result.success);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn browser_script_skill_tool_executes_packaged_script_via_eval() {
|
||||
let skill_dir = unique_temp_dir("sgclaw-browser-script-skill");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::net::TcpListener;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
@@ -208,6 +209,84 @@ fn parse_probe_args_defaults_timeout_when_flag_is_omitted() {
|
||||
);
|
||||
}
|
||||
|
||||
fn temp_step_file(name: &str, contents: &str) -> PathBuf {
|
||||
let mut path = std::env::temp_dir();
|
||||
let nanos = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
path.push(format!("sgclaw-ws-probe-{name}-{nanos}.txt"));
|
||||
std::fs::write(&path, contents).unwrap();
|
||||
path
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_probe_args_accepts_step_file() {
|
||||
let step_file = temp_step_file(
|
||||
"single",
|
||||
"open-agent::[\"about:blank\",\"sgOpenAgent\"]\n",
|
||||
);
|
||||
let args = vec![
|
||||
"--ws-url".to_string(),
|
||||
"ws://127.0.0.1:12345".to_string(),
|
||||
"--step-file".to_string(),
|
||||
step_file.display().to_string(),
|
||||
];
|
||||
|
||||
let parsed = parse_probe_args(&args).unwrap();
|
||||
|
||||
assert_eq!(parsed.ws_url, "ws://127.0.0.1:12345");
|
||||
assert_eq!(parsed.timeout_ms, 1500);
|
||||
assert_eq!(
|
||||
parsed.steps,
|
||||
vec![ProbeStep {
|
||||
label: "open-agent".to_string(),
|
||||
payload: "[\"about:blank\",\"sgOpenAgent\"]".to_string(),
|
||||
expect_reply: true,
|
||||
}]
|
||||
);
|
||||
|
||||
let _ = std::fs::remove_file(step_file);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_probe_args_preserves_step_and_step_file_order() {
|
||||
let step_file = temp_step_file(
|
||||
"ordered",
|
||||
"open-hot::[\"about:blank\",\"sgBrowerserOpenPage\",\"https://www.zhihu.com/hot\"]\n",
|
||||
);
|
||||
let args = vec![
|
||||
"--ws-url".to_string(),
|
||||
"ws://127.0.0.1:12345".to_string(),
|
||||
"--step".to_string(),
|
||||
"open-agent::[\"about:blank\",\"sgOpenAgent\"]".to_string(),
|
||||
"--step-file".to_string(),
|
||||
step_file.display().to_string(),
|
||||
];
|
||||
|
||||
let parsed = parse_probe_args(&args).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
parsed.steps,
|
||||
vec![
|
||||
ProbeStep {
|
||||
label: "open-agent".to_string(),
|
||||
payload: "[\"about:blank\",\"sgOpenAgent\"]".to_string(),
|
||||
expect_reply: true,
|
||||
},
|
||||
ProbeStep {
|
||||
label: "open-hot".to_string(),
|
||||
payload:
|
||||
"[\"about:blank\",\"sgBrowerserOpenPage\",\"https://www.zhihu.com/hot\"]"
|
||||
.to_string(),
|
||||
expect_reply: true,
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
let _ = std::fs::remove_file(step_file);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn probe_records_welcome_then_silence_transcript() {
|
||||
let steps = vec![
|
||||
|
||||
@@ -321,6 +321,35 @@ fn sgclaw_settings_load_service_ws_listen_addr_from_browser_config() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sgclaw_settings_load_scheduled_monitoring_platform_service_base_from_browser_config() {
|
||||
let root = std::env::temp_dir().join(format!("sgclaw-monitoring-platform-config-{}", Uuid::new_v4()));
|
||||
fs::create_dir_all(&root).unwrap();
|
||||
let config_path = root.join("sgclaw_config.json");
|
||||
|
||||
fs::write(
|
||||
&config_path,
|
||||
r#"{
|
||||
"apiKey": "sk-runtime",
|
||||
"baseUrl": "https://api.deepseek.com",
|
||||
"model": "deepseek-chat",
|
||||
"scheduledMonitoringPlatformServiceBaseUrl": "http://25.215.213.128:18080"
|
||||
}"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let settings = SgClawSettings::load(Some(config_path.as_path()))
|
||||
.unwrap()
|
||||
.expect("expected sgclaw settings from config file");
|
||||
|
||||
assert_eq!(
|
||||
settings
|
||||
.scheduled_monitoring_platform_service_base_url
|
||||
.as_deref(),
|
||||
Some("http://25.215.213.128:18080")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn browser_attached_config_uses_low_temperature_for_deterministic_execution() {
|
||||
let settings = SgClawSettings::from_legacy_deepseek_fields(
|
||||
|
||||
29
tests/fixtures/generated_scene/bootstrap_localhost_pollution/index.html
vendored
Normal file
29
tests/fixtures/generated_scene/bootstrap_localhost_pollution/index.html
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Bootstrap Localhost Pollution Fixture</title>
|
||||
</head>
|
||||
<body>
|
||||
<script src="http://cdn.example.com/app.js"></script>
|
||||
<script>
|
||||
const sourceUrl = "http://yx.gs.sgcc.com.cn";
|
||||
const requestUrl = "http://localhost:13313/browser-helper.html";
|
||||
|
||||
async function getUserList() {
|
||||
return $.ajax({
|
||||
url: "http://yxgateway.gs.sgcc.com.cn/marketing/userList",
|
||||
type: "POST",
|
||||
contentType: "application/json"
|
||||
});
|
||||
}
|
||||
|
||||
function exportExcel() {
|
||||
return $.ajax({
|
||||
url: "http://localhost:13313/SurfaceServices/personalBread/export/faultDetailsExportXLSX",
|
||||
type: "POST"
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
52
tests/fixtures/generated_scene/bootstrap_target_normalization_followup_2026-04-19.json
vendored
Normal file
52
tests/fixtures/generated_scene/bootstrap_target_normalization_followup_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"runDate": "2026-04-19",
|
||||
"plan": "2026-04-19-bootstrap-target-normalization-roadmap-plan.md",
|
||||
"parentFramework": "2026-04-19-scene-skill-102-full-coverage-framework-plan.md",
|
||||
"parentSequence": "2026-04-19-final-2-residual-child-plan-sequence-plan.md",
|
||||
"fixedInputBucket": [
|
||||
"sweep-091-scene"
|
||||
],
|
||||
"officialBoardUpdated": false,
|
||||
"implementationSlice": {
|
||||
"type": "none",
|
||||
"summary": "The fixed scene closed under the current deterministic bootstrap target recovery path; no analyzer/generator change was required in this plan."
|
||||
},
|
||||
"summary": {
|
||||
"totalScenes": 1,
|
||||
"autoPass": 1,
|
||||
"failClosedKnown": 0,
|
||||
"sourceUnreadable": 0,
|
||||
"unknown": 0
|
||||
},
|
||||
"scenes": [
|
||||
{
|
||||
"sceneId": "sweep-091-scene",
|
||||
"sceneName": "配网异常设备监控统计",
|
||||
"previousFrameworkStatus": "framework-structured-fail-closed",
|
||||
"previousWorkflowArchetype": "page_state_eval",
|
||||
"rawStatus": "auto-pass",
|
||||
"workflowArchetype": "single_request_enrichment",
|
||||
"readinessLevel": "A",
|
||||
"bootstrap": {
|
||||
"expectedDomain": "21.77.244.194:18890",
|
||||
"targetUrl": "http://21.77.244.194:18890/mainSystem",
|
||||
"source": "deterministic"
|
||||
},
|
||||
"missingPieces": [],
|
||||
"risks": [],
|
||||
"passedGates": [
|
||||
"bootstrap_resolved",
|
||||
"workflow_contract_complete",
|
||||
"runtime_contract_compatible",
|
||||
"g1e_scope_compatible"
|
||||
],
|
||||
"skillDir": "examples/bootstrap_target_normalization_followup_2026-04-19/skills/sweep-091-scene",
|
||||
"reportPath": "examples/bootstrap_target_normalization_followup_2026-04-19/skills/sweep-091-scene/references/generation-report.json"
|
||||
}
|
||||
],
|
||||
"notes": [
|
||||
"This follow-up uses the fixed official-board source scene for sweep-091-scene: 配网异常设备监控统计.",
|
||||
"Earlier residual reports included a scene-name drift for the same sweep id; this plan does not edit historical assets.",
|
||||
"Official board update is intentionally deferred to final-2 board reconciliation refresh."
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"runDate": "2026-04-19",
|
||||
"plan": "2026-04-19-bootstrap-target-normalization-roadmap-plan.md",
|
||||
"policySource": "tests/fixtures/generated_scene/promotion_board_reconciliation_policy_2026-04-19.json",
|
||||
"followupSource": "tests/fixtures/generated_scene/bootstrap_target_normalization_followup_2026-04-19.json",
|
||||
"totalScenes": 1,
|
||||
"summary": {
|
||||
"framework-auto-pass-candidate": 1,
|
||||
"framework-structured-fail-closed": 0,
|
||||
"source-unreadable": 0,
|
||||
"unresolved-followup-status": 0
|
||||
},
|
||||
"officialBoardUpdated": false,
|
||||
"canAutoUpdateBoard": false,
|
||||
"scenes": [
|
||||
{
|
||||
"sceneId": "sweep-091-scene",
|
||||
"sceneName": "配网异常设备监控统计",
|
||||
"rawStatus": "auto-pass",
|
||||
"reconciliationCandidateStatus": "framework-auto-pass-candidate",
|
||||
"workflowArchetype": "single_request_enrichment",
|
||||
"readinessLevel": "A",
|
||||
"decisionOverlay": null,
|
||||
"nextAction": null,
|
||||
"canAutoUpdateBoard": false,
|
||||
"policyReason": "Route 6 policy requires a dedicated board reconciliation plan; bootstrap target normalization only publishes candidates."
|
||||
}
|
||||
]
|
||||
}
|
||||
35
tests/fixtures/generated_scene/bootstrap_target_residual_isolation_2026-04-19.json
vendored
Normal file
35
tests/fixtures/generated_scene/bootstrap_target_residual_isolation_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"runDate": "2026-04-19",
|
||||
"parentPlan": "docs/superpowers/plans/2026-04-19-bootstrap-target-residual-isolation-plan.md",
|
||||
"source": "tests/fixtures/generated_scene/full_coverage_reconciliation_candidates_2026-04-19.json",
|
||||
"scope": {
|
||||
"isolationOnly": true,
|
||||
"forbiddenImplementation": [
|
||||
"src/generated_scene/analyzer.rs",
|
||||
"src/generated_scene/generator.rs",
|
||||
"login/runtime implementation files",
|
||||
"tests/fixtures/generated_scene/scene_execution_board_2026-04-18.json"
|
||||
]
|
||||
},
|
||||
"summary": {
|
||||
"total": 1,
|
||||
"isolatedBootstrapTargetResidual": 1,
|
||||
"loginRecoveryStarted": false,
|
||||
"runtimeNavigationImplementationStarted": false
|
||||
},
|
||||
"decisions": [
|
||||
{
|
||||
"index": 91,
|
||||
"sceneId": "sweep-091-scene",
|
||||
"sceneName": "用户停电频次分析监测",
|
||||
"workflowArchetype": "page_state_eval",
|
||||
"rawSweepStatus": "fail-closed-known",
|
||||
"reconciliationCandidateStatus": "framework-structured-fail-closed",
|
||||
"readinessLevel": "C",
|
||||
"decision": "isolate-bootstrap-target-residual",
|
||||
"reason": "Page-state/bootstrap-target residual requires navigation/login target normalization and must not be mixed with contract recovery work.",
|
||||
"nextAction": "future-bootstrap-target-normalization-roadmap-input"
|
||||
}
|
||||
],
|
||||
"stopStatement": "Route D is isolation-complete. Do not begin login recovery or runtime navigation implementation under this plan."
|
||||
}
|
||||
56
tests/fixtures/generated_scene/boundary_fail_closed_decision_2026-04-19.json
vendored
Normal file
56
tests/fixtures/generated_scene/boundary_fail_closed_decision_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"runDate": "2026-04-19",
|
||||
"parentFramework": "2026-04-19-scene-skill-102-full-coverage-framework-plan",
|
||||
"parentRoute": "Route 5 / boundary-family fail-closed",
|
||||
"plan": "2026-04-19-boundary-fail-closed-decision-plan.md",
|
||||
"scope": {
|
||||
"decisionOnly": true,
|
||||
"fixedInputBuckets": {
|
||||
"local_doc_pipeline": 5,
|
||||
"host_bridge_workflow": 1,
|
||||
"page_state_eval_bootstrap_target": 1
|
||||
},
|
||||
"forbiddenChanges": [
|
||||
"src/generated_scene/analyzer.rs",
|
||||
"src/generated_scene/generator.rs",
|
||||
"tests/fixtures/generated_scene/scene_execution_board_2026-04-18.json",
|
||||
"boundary implementation"
|
||||
]
|
||||
},
|
||||
"summary": {
|
||||
"totalBoundaryBuckets": 3,
|
||||
"totalBoundaryRecords": 7,
|
||||
"openImplementationSlices": 0,
|
||||
"heldOrDeferredRecords": 7,
|
||||
"unresolvedBoundaryAmbiguity": 0
|
||||
},
|
||||
"decisions": [
|
||||
{
|
||||
"bucket": "local_doc_pipeline",
|
||||
"count": 5,
|
||||
"decision": "hold-as-boundary-fail-closed",
|
||||
"reason": "G8 local document pipeline requires local document runtime and attachment handling beyond current full-coverage framework implementation routes.",
|
||||
"nextAction": "defer until a dedicated local-doc runtime roadmap is authorized"
|
||||
},
|
||||
{
|
||||
"bucket": "host_bridge_workflow",
|
||||
"count": 1,
|
||||
"decision": "hold-as-boundary-fail-closed",
|
||||
"reason": "The remaining G6-style boundary record depends on host bridge execution semantics beyond the current decision-only Route 5 scope.",
|
||||
"nextAction": "defer until a dedicated host-bridge implementation roadmap is authorized"
|
||||
},
|
||||
{
|
||||
"bucket": "page_state_eval/bootstrap_target",
|
||||
"count": 1,
|
||||
"decision": "isolate-bootstrap-target",
|
||||
"reason": "Bootstrap target resolution is a separate navigation/login target problem and must not be mixed with contract recovery work.",
|
||||
"nextAction": "keep isolated for a future bootstrap-target roadmap"
|
||||
}
|
||||
],
|
||||
"completionCriteria": {
|
||||
"everyRoute5SubgroupHasNamedDecision": true,
|
||||
"followUpBoundedPlanIsOptional": true,
|
||||
"boundaryImplementationStarted": false
|
||||
},
|
||||
"stopStatement": "Route 5 is decision-complete. Do not begin boundary implementation under this plan."
|
||||
}
|
||||
63
tests/fixtures/generated_scene/boundary_family_entry_decision_2026-04-19.json
vendored
Normal file
63
tests/fixtures/generated_scene/boundary_family_entry_decision_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"decisionDate": "2026-04-19",
|
||||
"scope": "boundary-family-real-sample-entry-roadmap",
|
||||
"startingState": {
|
||||
"mainlineClosed": [
|
||||
"G1-E",
|
||||
"G2",
|
||||
"G3"
|
||||
],
|
||||
"boundaryHeld": [
|
||||
"G6",
|
||||
"G7",
|
||||
"G8"
|
||||
],
|
||||
"deferredOutOfScope": [
|
||||
"G4",
|
||||
"G5"
|
||||
]
|
||||
},
|
||||
"comparisonMatrix": [
|
||||
{
|
||||
"group": "G6",
|
||||
"representativeScene": "电能表现场检验完成率指标报表",
|
||||
"entryCondition": "A real sample requires host bridge execution semantics beyond current repo-local validation.",
|
||||
"smallestNewCapability": "host bridge real-sample execution semantics",
|
||||
"entryCost": "high",
|
||||
"decision": "hold",
|
||||
"reason": "Requires host-side execution semantics before a real-sample slice can be bounded safely."
|
||||
},
|
||||
{
|
||||
"group": "G7",
|
||||
"representativeScene": "计量资产库存统计",
|
||||
"entryCondition": "A real sample requires multi-endpoint aggregation verification beyond fixture-level coverage.",
|
||||
"smallestNewCapability": "real multi-endpoint aggregation verification",
|
||||
"entryCost": "medium",
|
||||
"decision": "selected",
|
||||
"reason": "Already has a minimal runnable runtime contract and needs the smallest new capability among boundary families."
|
||||
},
|
||||
{
|
||||
"group": "G8",
|
||||
"representativeScene": "95598供电服务月报",
|
||||
"entryCondition": "A real sample requires local document pipeline runtime and attachment handling beyond current repo-local coverage.",
|
||||
"smallestNewCapability": "local document pipeline runtime and attachment handling",
|
||||
"entryCost": "high",
|
||||
"decision": "hold",
|
||||
"reason": "Still depends on local pipeline and attachment behavior that exceeds the bounded next-step budget."
|
||||
}
|
||||
],
|
||||
"selectedCandidate": {
|
||||
"group": "G7",
|
||||
"representativeScene": "计量资产库存统计",
|
||||
"nextDesign": "docs/superpowers/specs/2026-04-19-g7-real-sample-entry-design.md",
|
||||
"nextPlan": "docs/superpowers/plans/2026-04-19-g7-real-sample-entry-plan.md"
|
||||
},
|
||||
"holdReasons": [
|
||||
"G6 remains held because host bridge execution semantics are still a stronger prerequisite than a bounded real-sample slice allows.",
|
||||
"G8 remains held because local document pipeline runtime and attachment handling are still outside the next bounded slice."
|
||||
],
|
||||
"notes": [
|
||||
"Only one boundary family is admitted as the next candidate.",
|
||||
"This decision does not execute a real sample yet; it only selects the next bounded direction."
|
||||
]
|
||||
}
|
||||
90
tests/fixtures/generated_scene/boundary_residual_hold_decision_2026-04-19.json
vendored
Normal file
90
tests/fixtures/generated_scene/boundary_residual_hold_decision_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
{
|
||||
"runDate": "2026-04-19",
|
||||
"parentPlan": "docs/superpowers/plans/2026-04-19-boundary-residual-hold-decision-plan.md",
|
||||
"source": "tests/fixtures/generated_scene/full_coverage_reconciliation_candidates_2026-04-19.json",
|
||||
"scope": {
|
||||
"decisionOnly": true,
|
||||
"forbiddenImplementation": [
|
||||
"src/generated_scene/analyzer.rs",
|
||||
"src/generated_scene/generator.rs",
|
||||
"tests/fixtures/generated_scene/scene_execution_board_2026-04-18.json"
|
||||
]
|
||||
},
|
||||
"summary": {
|
||||
"total": 6,
|
||||
"localDocPipeline": 5,
|
||||
"hostBridgeWorkflow": 1,
|
||||
"heldOrDeferred": 6,
|
||||
"implementationStarted": false,
|
||||
"unresolvedBoundaryAmbiguity": 0
|
||||
},
|
||||
"decisions": [
|
||||
{
|
||||
"index": 33,
|
||||
"sceneId": "sweep-033-scene",
|
||||
"sceneName": "供电可靠率指标统计表",
|
||||
"workflowArchetype": "local_doc_pipeline",
|
||||
"rawSweepStatus": "fail-closed-known",
|
||||
"readinessLevel": "C",
|
||||
"decision": "hold-for-local-doc-runtime-roadmap",
|
||||
"reason": "Local document pipeline residual requires local document runtime and attachment/document handling that is outside residual Route C implementation scope.",
|
||||
"nextAction": "future-local-doc-runtime-roadmap-input"
|
||||
},
|
||||
{
|
||||
"index": 34,
|
||||
"sceneId": "sweep-034-scene",
|
||||
"sceneName": "供电可靠性数据质量自查报告月报",
|
||||
"workflowArchetype": "local_doc_pipeline",
|
||||
"rawSweepStatus": "fail-closed-known",
|
||||
"readinessLevel": "C",
|
||||
"decision": "hold-for-local-doc-runtime-roadmap",
|
||||
"reason": "Local document pipeline residual requires local document runtime and attachment/document handling that is outside residual Route C implementation scope.",
|
||||
"nextAction": "future-local-doc-runtime-roadmap-input"
|
||||
},
|
||||
{
|
||||
"index": 42,
|
||||
"sceneId": "sweep-042-scene",
|
||||
"sceneName": "国网金昌供电公司营商环境周例会报告",
|
||||
"workflowArchetype": "local_doc_pipeline",
|
||||
"rawSweepStatus": "fail-closed-known",
|
||||
"readinessLevel": "C",
|
||||
"decision": "hold-for-local-doc-runtime-roadmap",
|
||||
"reason": "Local document pipeline residual requires local document runtime and attachment/document handling that is outside residual Route C implementation scope.",
|
||||
"nextAction": "future-local-doc-runtime-roadmap-input"
|
||||
},
|
||||
{
|
||||
"index": 51,
|
||||
"sceneId": "sweep-051-scene",
|
||||
"sceneName": "嘉峪关可靠性分析报告",
|
||||
"workflowArchetype": "local_doc_pipeline",
|
||||
"rawSweepStatus": "fail-closed-known",
|
||||
"readinessLevel": "C",
|
||||
"decision": "hold-for-local-doc-runtime-roadmap",
|
||||
"reason": "Local document pipeline residual requires local document runtime and attachment/document handling that is outside residual Route C implementation scope.",
|
||||
"nextAction": "future-local-doc-runtime-roadmap-input"
|
||||
},
|
||||
{
|
||||
"index": 74,
|
||||
"sceneId": "sweep-074-scene",
|
||||
"sceneName": "同兴智能安全督查日报",
|
||||
"workflowArchetype": "local_doc_pipeline",
|
||||
"rawSweepStatus": "fail-closed-known",
|
||||
"readinessLevel": "C",
|
||||
"decision": "hold-for-local-doc-runtime-roadmap",
|
||||
"reason": "Local document pipeline residual requires local document runtime and attachment/document handling that is outside residual Route C implementation scope.",
|
||||
"nextAction": "future-local-doc-runtime-roadmap-input"
|
||||
},
|
||||
{
|
||||
"index": 85,
|
||||
"sceneId": "sweep-085-scene",
|
||||
"sceneName": "业扩报装管理制度",
|
||||
"workflowArchetype": "host_bridge_workflow",
|
||||
"rawSweepStatus": "fail-closed-known",
|
||||
"readinessLevel": "C",
|
||||
"decision": "hold-for-host-bridge-runtime-roadmap",
|
||||
"reason": "Host bridge workflow residual requires host bridge execution semantics beyond this decision-only route.",
|
||||
"nextAction": "future-host-bridge-runtime-roadmap-input"
|
||||
}
|
||||
],
|
||||
"stopStatement": "Route C is decision-complete. Do not begin boundary implementation under this plan."
|
||||
}
|
||||
97
tests/fixtures/generated_scene/boundary_runtime_entry_rules_2026-04-18.json
vendored
Normal file
97
tests/fixtures/generated_scene/boundary_runtime_entry_rules_2026-04-18.json
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
{
|
||||
"assetDate": "2026-04-19",
|
||||
"scope": "post-roadmap-boundary-and-runtime-entry-rules",
|
||||
"boundaryReadiness": [
|
||||
{
|
||||
"group": "G6",
|
||||
"status": "boundary-family-established",
|
||||
"readiness": "executed-pass",
|
||||
"nextEntryCondition": "The fixed G6 real sample has now executed as host_bridge_workflow; keep further host-bridge work bounded to a new roadmap."
|
||||
},
|
||||
{
|
||||
"group": "G7",
|
||||
"status": "boundary-family-established",
|
||||
"readiness": "executed-pass",
|
||||
"nextEntryCondition": "The first fixed G7 real sample has now executed as multi_endpoint_inventory; keep further boundary-family work bounded to a new roadmap."
|
||||
},
|
||||
{
|
||||
"group": "G8",
|
||||
"status": "boundary-family-established",
|
||||
"readiness": "hold-as-boundary",
|
||||
"nextEntryCondition": "A real sample requires local document pipeline runtime and attachment handling beyond current repo-local coverage."
|
||||
}
|
||||
],
|
||||
"deferredFamilyEntryCriteria": [
|
||||
{
|
||||
"group": "G4",
|
||||
"currentStatus": "deferred",
|
||||
"entryCriteria": [
|
||||
"at least one stable candidate cluster in the 102-scene execution board",
|
||||
"a family-level contract that is distinct from existing G1/G2/G3/G6/G7/G8 contracts",
|
||||
"a prioritization reason written into the next roadmap"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "G5",
|
||||
"currentStatus": "degraded",
|
||||
"entryCriteria": [
|
||||
"a clear reason why degraded handling is insufficient",
|
||||
"a bounded implementation slice that does not reopen the completed roadmap",
|
||||
"runtime gaps needed by the family are explicitly classified first"
|
||||
]
|
||||
}
|
||||
],
|
||||
"runtimeGapMatrix": [
|
||||
{
|
||||
"gap": "login-recovery",
|
||||
"category": "runtime-platform-gap",
|
||||
"status": "planned-not-implemented",
|
||||
"blocks": [
|
||||
"real-world end-to-end execution outside repo-local fixture scope"
|
||||
]
|
||||
},
|
||||
{
|
||||
"gap": "host-runtime-integration",
|
||||
"category": "runtime-platform-gap",
|
||||
"status": "partially-covered-by-boundary-families",
|
||||
"blocks": [
|
||||
"future host-bridge rollout beyond the bounded G6 real-sample entry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"gap": "transport-runtime",
|
||||
"category": "runtime-platform-gap",
|
||||
"status": "planned-not-implemented",
|
||||
"blocks": [
|
||||
"direct post-roadmap runtime rollout"
|
||||
]
|
||||
},
|
||||
{
|
||||
"gap": "local-doc-and-attachment-workflows",
|
||||
"category": "runtime-platform-gap",
|
||||
"status": "partially-covered-by-G8-fixture-only",
|
||||
"blocks": [
|
||||
"G8 real-sample execution"
|
||||
]
|
||||
},
|
||||
{
|
||||
"gap": "g3-real-sample-output-contract-verification",
|
||||
"category": "mainline-contract-gap",
|
||||
"status": "closed-in-mainline",
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"gap": "g2-real-sample-contract-correction",
|
||||
"category": "mainline-contract-gap",
|
||||
"status": "closed-in-mainline",
|
||||
"blocks": []
|
||||
}
|
||||
],
|
||||
"prioritization": [
|
||||
"Both G3 and G2 mainline real-sample contract gaps are now closed in the validation layer.",
|
||||
"G7 is now the first boundary family with an executed-pass real-sample record.",
|
||||
"G6 is now the second boundary family with an executed-pass real-sample record.",
|
||||
"Do not open G4 or G5 without a new bounded roadmap that identifies the next mainline pressure explicitly.",
|
||||
"Keep G8 as a boundary family until a new roadmap promotes it into real-sample execution scope."
|
||||
]
|
||||
}
|
||||
50
tests/fixtures/generated_scene/boundary_runtime_prerequisites_decision_2026-04-19.json
vendored
Normal file
50
tests/fixtures/generated_scene/boundary_runtime_prerequisites_decision_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"decisionDate": "2026-04-19",
|
||||
"scope": "boundary-runtime-prerequisites-roadmap",
|
||||
"startingState": {
|
||||
"boundaryClosed": [
|
||||
"G7"
|
||||
],
|
||||
"boundaryHeld": [
|
||||
"G6",
|
||||
"G8"
|
||||
],
|
||||
"mainlineClosed": [
|
||||
"G1-E",
|
||||
"G2",
|
||||
"G3"
|
||||
],
|
||||
"deferredOutOfScope": [
|
||||
"G4",
|
||||
"G5"
|
||||
]
|
||||
},
|
||||
"comparisonMatrix": [
|
||||
{
|
||||
"direction": "G6-host-bridge-prerequisites",
|
||||
"blockedCapability": "host bridge real-sample execution semantics",
|
||||
"isolationCost": "medium",
|
||||
"decision": "selected",
|
||||
"reason": "It concentrates on one prerequisite line and is narrower than the combined local-doc plus attachment burden."
|
||||
},
|
||||
{
|
||||
"direction": "G8-local-doc-prerequisites",
|
||||
"blockedCapability": "local document runtime and attachment handling",
|
||||
"isolationCost": "high",
|
||||
"decision": "hold",
|
||||
"reason": "It still combines local pipeline runtime and attachment handling, which is broader than the next bounded slice should absorb."
|
||||
}
|
||||
],
|
||||
"selectedDirection": {
|
||||
"direction": "G6-host-bridge-prerequisites",
|
||||
"nextDesign": "docs/superpowers/specs/2026-04-19-g6-host-bridge-prerequisites-design.md",
|
||||
"nextPlan": "docs/superpowers/plans/2026-04-19-g6-host-bridge-prerequisites-plan.md"
|
||||
},
|
||||
"holdReasons": [
|
||||
"G8 remains held because local document runtime and attachment handling still form a heavier prerequisite bundle than G6 host-bridge semantics."
|
||||
],
|
||||
"notes": [
|
||||
"The roadmap selects one prerequisite direction only.",
|
||||
"No boundary family execution is opened under this roadmap."
|
||||
]
|
||||
}
|
||||
132
tests/fixtures/generated_scene/deterministic_keyword_scoring_refinement_2026-04-20.json
vendored
Normal file
132
tests/fixtures/generated_scene/deterministic_keyword_scoring_refinement_2026-04-20.json
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
{
|
||||
"runDate": "2026-04-20",
|
||||
"plan": "2026-04-20-deterministic-keyword-scoring-refinement-plan.md",
|
||||
"summary": {
|
||||
"totalCompletePackages": 101,
|
||||
"beforeReady": 92,
|
||||
"beforeAmbiguous": 9,
|
||||
"afterReady": 101,
|
||||
"afterAmbiguous": 0,
|
||||
"afterNoMatch": 0,
|
||||
"afterOther": 0,
|
||||
"fixedGapCount": 9,
|
||||
"fixedGapResolved": 9
|
||||
},
|
||||
"decisions": [
|
||||
{
|
||||
"sceneId": "sweep-026-scene",
|
||||
"sceneName": "县区公司故障明细",
|
||||
"newKeywords": [
|
||||
"县区公司故障明细"
|
||||
],
|
||||
"excludeKeywords": [],
|
||||
"decision": "full-scene-name-only-with-pair-specific-excludes"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-034-scene",
|
||||
"sceneName": "售电收入日统计排程预测",
|
||||
"newKeywords": [
|
||||
"售电收入日统计排程预测"
|
||||
],
|
||||
"excludeKeywords": [],
|
||||
"decision": "full-scene-name-only-with-pair-specific-excludes"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-037-scene",
|
||||
"sceneName": "嘉峪关可靠性分析报告",
|
||||
"newKeywords": [
|
||||
"嘉峪关可靠性分析报告"
|
||||
],
|
||||
"excludeKeywords": [],
|
||||
"decision": "full-scene-name-only-with-pair-specific-excludes"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-038-scene",
|
||||
"sceneName": "嘉峪关周报",
|
||||
"newKeywords": [
|
||||
"嘉峪关周报"
|
||||
],
|
||||
"excludeKeywords": [],
|
||||
"decision": "full-scene-name-only-with-pair-specific-excludes"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-039-scene",
|
||||
"sceneName": "嘉峪关故障明细",
|
||||
"newKeywords": [
|
||||
"嘉峪关故障明细"
|
||||
],
|
||||
"excludeKeywords": [],
|
||||
"decision": "full-scene-name-only-with-pair-specific-excludes"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-040-scene",
|
||||
"sceneName": "嘉峪关日报",
|
||||
"newKeywords": [
|
||||
"嘉峪关日报"
|
||||
],
|
||||
"excludeKeywords": [],
|
||||
"decision": "full-scene-name-only-with-pair-specific-excludes"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-041-scene",
|
||||
"sceneName": "嘉峪关月报",
|
||||
"newKeywords": [
|
||||
"嘉峪关月报"
|
||||
],
|
||||
"excludeKeywords": [],
|
||||
"decision": "full-scene-name-only-with-pair-specific-excludes"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-044-scene",
|
||||
"sceneName": "国网金昌供电公司指挥中心生产例会报告",
|
||||
"newKeywords": [
|
||||
"国网金昌供电公司指挥中心生产例会报告"
|
||||
],
|
||||
"excludeKeywords": [],
|
||||
"decision": "full-scene-name-only-with-pair-specific-excludes"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-045-scene",
|
||||
"sceneName": "国网金昌供电公司营商环境周例会报告",
|
||||
"newKeywords": [
|
||||
"国网金昌供电公司营商环境周例会报告"
|
||||
],
|
||||
"excludeKeywords": [],
|
||||
"decision": "full-scene-name-only-with-pair-specific-excludes"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-097-scene",
|
||||
"sceneName": "重要服务事项报备统计",
|
||||
"newKeywords": [
|
||||
"重要服务事项报备统计"
|
||||
],
|
||||
"excludeKeywords": [
|
||||
"95598"
|
||||
],
|
||||
"decision": "full-scene-name-only-with-pair-specific-excludes"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-059-scene",
|
||||
"sceneName": "故障明细",
|
||||
"newKeywords": [
|
||||
"故障明细"
|
||||
],
|
||||
"excludeKeywords": [
|
||||
"县区公司",
|
||||
"嘉峪关"
|
||||
],
|
||||
"decision": "full-scene-name-only-with-pair-specific-excludes"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-033-scene",
|
||||
"sceneName": "售电收入日统计",
|
||||
"newKeywords": [
|
||||
"售电收入日统计"
|
||||
],
|
||||
"excludeKeywords": [
|
||||
"排程预测"
|
||||
],
|
||||
"decision": "full-scene-name-only-with-pair-specific-excludes"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -2,10 +2,20 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>测试外部脚本提取</title>
|
||||
<title>External Script Bootstrap Fixture</title>
|
||||
<script src="http://25.215.213.128:18080/a_js/YPTAPI.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">测试页面</div>
|
||||
<div id="app">Fixture</div>
|
||||
<script>
|
||||
const sourceUrl = "http://yx.gs.sgcc.com.cn";
|
||||
function queryData() {
|
||||
return $.ajax({
|
||||
url: "http://yx.gs.sgcc.com.cn/report/query",
|
||||
type: "POST",
|
||||
contentType: "application/json"
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
80
tests/fixtures/generated_scene/family_expansion_policy.json
vendored
Normal file
80
tests/fixtures/generated_scene/family_expansion_policy.json
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
{
|
||||
"policyVersion": "2026-04-18",
|
||||
"scope": "roadmap-plan-track-e",
|
||||
"mainlineGroups": [
|
||||
{
|
||||
"group": "G1",
|
||||
"policy": "mainline",
|
||||
"executionRule": "build reusable single-request template and validate representative migrations first",
|
||||
"acceptanceFocus": [
|
||||
"request/response/normalize restored",
|
||||
"single_request_table compile path stable",
|
||||
"family-level reuse confirmed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "G2",
|
||||
"policy": "mainline",
|
||||
"executionRule": "deepen multi-mode semantic recovery before broad expansion",
|
||||
"acceptanceFocus": [
|
||||
"mode matrix restored",
|
||||
"mode-specific request/response contract restored",
|
||||
"bootstrap business context stable"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "G3",
|
||||
"policy": "mainline",
|
||||
"executionRule": "prioritize fail-closed and host-runtime separation before expansion",
|
||||
"acceptanceFocus": [
|
||||
"pagination chain restored",
|
||||
"secondary request chain restored",
|
||||
"localhost host-runtime evidence separated from business bootstrap"
|
||||
]
|
||||
}
|
||||
],
|
||||
"boundaryGroups": [
|
||||
{
|
||||
"group": "G6",
|
||||
"policy": "boundary-runtime",
|
||||
"executionRule": "treat as independent family with explicit host-bridge runtime contract; do not fold back into G1/G1-E/G3",
|
||||
"acceptanceFocus": [
|
||||
"host bridge action evidence restored",
|
||||
"callback request chain present",
|
||||
"incomplete manual scene IR stays fail-closed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "G7",
|
||||
"policy": "boundary-runtime",
|
||||
"executionRule": "treat as independent family with multi-endpoint aggregation contract; do not fold back into G1/G1-E",
|
||||
"acceptanceFocus": [
|
||||
"inventory endpoint set restored",
|
||||
"inventory aggregate step present",
|
||||
"incomplete manual scene IR stays fail-closed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "G8",
|
||||
"policy": "boundary-runtime",
|
||||
"executionRule": "treat as independent family with local pipeline contract; do not collapse into G6 host bridge workflow",
|
||||
"acceptanceFocus": [
|
||||
"localhost pipeline dependencies restored",
|
||||
"sql/doc export steps present",
|
||||
"incomplete manual scene IR stays fail-closed"
|
||||
]
|
||||
}
|
||||
],
|
||||
"deferredGroups": [
|
||||
{
|
||||
"group": "G4",
|
||||
"policy": "deferred",
|
||||
"executionRule": "keep extension entry only; do not consume current mainline capacity"
|
||||
},
|
||||
{
|
||||
"group": "G5",
|
||||
"policy": "degraded",
|
||||
"executionRule": "default fail-closed or analysis-only; do not pollute mainline archetypes"
|
||||
}
|
||||
]
|
||||
}
|
||||
79
tests/fixtures/generated_scene/final_2_official_board_reconciliation_refresh_2026-04-19.json
vendored
Normal file
79
tests/fixtures/generated_scene/final_2_official_board_reconciliation_refresh_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
{
|
||||
"runDate": "2026-04-19",
|
||||
"plan": "2026-04-19-final-2-official-board-reconciliation-refresh-plan.md",
|
||||
"inputCandidates": [
|
||||
"tests/fixtures/generated_scene/bootstrap_target_normalization_reconciliation_candidates_2026-04-19.json",
|
||||
"tests/fixtures/generated_scene/host_bridge_runtime_reconciliation_candidates_2026-04-19.json"
|
||||
],
|
||||
"officialBoard": "tests/fixtures/generated_scene/scene_execution_board_2026-04-18.json",
|
||||
"officialBoardUpdated": true,
|
||||
"updatedSceneCount": 2,
|
||||
"summary": {
|
||||
"totalScenes": 102,
|
||||
"frameworkStatusCounts": {
|
||||
"framework-auto-pass": 102,
|
||||
"framework-structured-fail-closed": 0,
|
||||
"framework-valid-host-bridge": 0,
|
||||
"source-unreadable": 0,
|
||||
"missing-source": 0,
|
||||
"unsupported-family": 0,
|
||||
"misclassified-unresolved": 0,
|
||||
"unresolved-followup-status": 0
|
||||
},
|
||||
"frameworkAutoPassCount": 102,
|
||||
"frameworkStructuredFailClosedCount": 0,
|
||||
"frameworkUnresolvedCount": 0
|
||||
},
|
||||
"updatedScenes": [
|
||||
{
|
||||
"source": "tests/fixtures/generated_scene/bootstrap_target_normalization_reconciliation_candidates_2026-04-19.json",
|
||||
"sceneId": "sweep-091-scene",
|
||||
"officialBoardSceneName": "配网异常设备监控统计",
|
||||
"candidateSceneName": "配网异常设备监控统计",
|
||||
"before": {
|
||||
"currentFrameworkStatus": "framework-auto-pass",
|
||||
"currentFrameworkCandidateStatus": "framework-auto-pass-candidate",
|
||||
"currentFrameworkArchetype": "single_request_enrichment",
|
||||
"currentFrameworkReadiness": "A",
|
||||
"currentFrameworkDecisionOverlay": null,
|
||||
"currentFrameworkNextAction": null
|
||||
},
|
||||
"after": {
|
||||
"currentFrameworkStatus": "framework-auto-pass",
|
||||
"currentFrameworkCandidateStatus": "framework-auto-pass-candidate",
|
||||
"currentFrameworkArchetype": "single_request_enrichment",
|
||||
"currentFrameworkReadiness": "A",
|
||||
"currentFrameworkDecisionOverlay": null,
|
||||
"currentFrameworkNextAction": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "tests/fixtures/generated_scene/host_bridge_runtime_reconciliation_candidates_2026-04-19.json",
|
||||
"sceneId": "sweep-085-scene",
|
||||
"officialBoardSceneName": "计量资产库存统计",
|
||||
"candidateSceneName": "计量资产库存统计",
|
||||
"before": {
|
||||
"currentFrameworkStatus": "framework-structured-fail-closed",
|
||||
"currentFrameworkCandidateStatus": "framework-structured-fail-closed",
|
||||
"currentFrameworkArchetype": "host_bridge_workflow",
|
||||
"currentFrameworkReadiness": "C",
|
||||
"currentFrameworkDecisionOverlay": "hold-for-host-bridge-runtime-roadmap",
|
||||
"currentFrameworkNextAction": "future-host-bridge-runtime-roadmap-input"
|
||||
},
|
||||
"after": {
|
||||
"currentFrameworkStatus": "framework-auto-pass",
|
||||
"currentFrameworkCandidateStatus": "framework-auto-pass-candidate",
|
||||
"currentFrameworkArchetype": "multi_endpoint_inventory",
|
||||
"currentFrameworkReadiness": "A",
|
||||
"currentFrameworkDecisionOverlay": null,
|
||||
"currentFrameworkNextAction": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"remainingStructuredFailClosed": [],
|
||||
"notes": [
|
||||
"Both final-2 candidate assets were consumed when present.",
|
||||
"Only framework-layer fields were updated.",
|
||||
"No analyzer/generator implementation was modified by this refresh."
|
||||
]
|
||||
}
|
||||
51
tests/fixtures/generated_scene/final_2_residual_roadmap_prioritization_2026-04-19.json
vendored
Normal file
51
tests/fixtures/generated_scene/final_2_residual_roadmap_prioritization_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"runDate": "2026-04-19",
|
||||
"plan": "2026-04-19-final-2-residual-roadmap-prioritization-plan.md",
|
||||
"parentFramework": "2026-04-19-scene-skill-102-full-coverage-framework-plan.md",
|
||||
"parentSequence": "2026-04-19-final-2-residual-child-plan-sequence-plan.md",
|
||||
"inputBoard": "tests/fixtures/generated_scene/scene_execution_board_2026-04-18.json",
|
||||
"totalResiduals": 2,
|
||||
"selectedRoadmap": "bootstrap target normalization roadmap",
|
||||
"selectedPlan": "2026-04-19-bootstrap-target-normalization-roadmap-plan.md",
|
||||
"candidates": [
|
||||
{
|
||||
"roadmap": "bootstrap target normalization roadmap",
|
||||
"plan": "2026-04-19-bootstrap-target-normalization-roadmap-plan.md",
|
||||
"sceneId": "sweep-091-scene",
|
||||
"sceneName": "配网异常设备监控统计",
|
||||
"currentFrameworkArchetype": "page_state_eval",
|
||||
"currentFrameworkReadiness": "C",
|
||||
"nextAction": "future-bootstrap-target-normalization-roadmap-input",
|
||||
"residualCount": 1,
|
||||
"scopeClarity": 4,
|
||||
"implementationRisk": 2,
|
||||
"regressionRisk": 2,
|
||||
"autoPassProbability": 3,
|
||||
"score": 13,
|
||||
"selected": true,
|
||||
"rationale": "Single-scene bootstrap target normalization is narrower than host-bridge runtime and does not require host transport semantics."
|
||||
},
|
||||
{
|
||||
"roadmap": "host-bridge runtime roadmap",
|
||||
"plan": "2026-04-19-host-bridge-runtime-roadmap-plan.md",
|
||||
"sceneId": "sweep-085-scene",
|
||||
"sceneName": "计量资产库存统计",
|
||||
"currentFrameworkArchetype": "host_bridge_workflow",
|
||||
"currentFrameworkReadiness": "C",
|
||||
"nextAction": "future-host-bridge-runtime-roadmap-input",
|
||||
"residualCount": 1,
|
||||
"scopeClarity": 3,
|
||||
"implementationRisk": 4,
|
||||
"regressionRisk": 4,
|
||||
"autoPassProbability": 3,
|
||||
"score": 7,
|
||||
"selected": false,
|
||||
"deferReason": "Host-bridge runtime can affect already passing host-bridge paths and should remain queued until the narrower bootstrap residual is handled."
|
||||
}
|
||||
],
|
||||
"coverageDelta": {
|
||||
"expectedNow": 0,
|
||||
"reason": "Decision-only plan"
|
||||
},
|
||||
"officialBoardUpdated": false
|
||||
}
|
||||
1562
tests/fixtures/generated_scene/final_coverage_status_rollup_2026-04-19.json
vendored
Normal file
1562
tests/fixtures/generated_scene/final_coverage_status_rollup_2026-04-19.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
tests/fixtures/generated_scene/full_coverage_followup_sweep_2026-04-19.json
vendored
Normal file
1
tests/fixtures/generated_scene/full_coverage_followup_sweep_2026-04-19.json
vendored
Normal file
File diff suppressed because one or more lines are too long
1
tests/fixtures/generated_scene/full_coverage_reconciliation_candidates_2026-04-19.json
vendored
Normal file
1
tests/fixtures/generated_scene/full_coverage_reconciliation_candidates_2026-04-19.json
vendored
Normal file
File diff suppressed because one or more lines are too long
9112
tests/fixtures/generated_scene/full_sweep_dry_run_2026-04-19.json
vendored
Normal file
9112
tests/fixtures/generated_scene/full_sweep_dry_run_2026-04-19.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1499
tests/fixtures/generated_scene/full_sweep_dry_run_triage_2026-04-19.json
vendored
Normal file
1499
tests/fixtures/generated_scene/full_sweep_dry_run_triage_2026-04-19.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1898
tests/fixtures/generated_scene/full_sweep_improvement_followup_2026-04-19.json
vendored
Normal file
1898
tests/fixtures/generated_scene/full_sweep_improvement_followup_2026-04-19.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2382
tests/fixtures/generated_scene/full_sweep_status_reconciliation_2026-04-19.json
vendored
Normal file
2382
tests/fixtures/generated_scene/full_sweep_status_reconciliation_2026-04-19.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
75
tests/fixtures/generated_scene/g1e_candidate_batch_2026-04-18.json
vendored
Normal file
75
tests/fixtures/generated_scene/g1e_candidate_batch_2026-04-18.json
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"batchId": "g1e-light-enrichment-candidates-2026-04-18",
|
||||
"family": "G1-E",
|
||||
"source": "docs/superpowers/reports/2026-04-18-g1-e-second-sample-reuse-report.md",
|
||||
"supportingSources": [
|
||||
"docs/superpowers/reports/2026-04-18-g1-e-p0-validation-report.md",
|
||||
"tests/fixtures/generated_scene/scene_ledger_status_2026-04-18.json"
|
||||
],
|
||||
"ledgerClusterLabel": "g1e-light-enrichment-candidate",
|
||||
"selectionRule": "roadmap Track B and Track D G1-E light enrichment samples promoted into reusable family baselines",
|
||||
"candidateCount": 3,
|
||||
"representativeBaseline": "tests/fixtures/generated_scene/g1e_light_enrichment",
|
||||
"promotedBatchExpansionBaselines": [
|
||||
{
|
||||
"fixtureDir": "tests/fixtures/generated_scene/g1e_light_enrichment_expansion",
|
||||
"sceneId": "p1-g1e-light-enrichment-expansion-report",
|
||||
"sceneName": "P1 G1-E light enrichment expansion report",
|
||||
"assertions": {
|
||||
"requiredMainRequest": "getWkorderAll",
|
||||
"requiredEnrichmentRequest": "queryMeterInfo",
|
||||
"requiredMergeJoinKey": "wkOrderNo",
|
||||
"requiredMergeAggregateRule": "group_by:countyCodeName",
|
||||
"requiredOutputColumn": "meterCapacityThisMonth"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fixtureDir": "tests/fixtures/generated_scene/g1e_light_enrichment_additional",
|
||||
"sceneId": "p1-g1e-light-enrichment-additional-report",
|
||||
"sceneName": "P1 G1-E light enrichment additional report",
|
||||
"assertions": {
|
||||
"requiredMainRequest": "getWkorderAll",
|
||||
"requiredEnrichmentRequest": "queryBusAcpt",
|
||||
"requiredMergeJoinKey": "wkOrderNo",
|
||||
"requiredMergeAggregateRule": "group_by:countyCodeName",
|
||||
"requiredOutputColumn": "batchCapacityThisMonth"
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectedSharedContract": {
|
||||
"archetype": "single_request_enrichment",
|
||||
"requiredGateNames": [
|
||||
"bootstrap_resolved",
|
||||
"request_contract_complete",
|
||||
"response_contract_complete",
|
||||
"workflow_contract_complete",
|
||||
"runtime_contract_compatible",
|
||||
"main_request_resolved",
|
||||
"enrichment_requests_resolved",
|
||||
"merge_plan_resolved",
|
||||
"g1e_scope_compatible"
|
||||
]
|
||||
},
|
||||
"candidates": [
|
||||
{
|
||||
"sceneKey": "high_low_voltage_new_capacity_monthly",
|
||||
"batchRole": "p0-anchor",
|
||||
"status": "promoted-baseline"
|
||||
},
|
||||
{
|
||||
"sceneKey": "light_enrichment_second_sample",
|
||||
"batchRole": "first-expansion-anchor",
|
||||
"status": "promoted-expansion"
|
||||
},
|
||||
{
|
||||
"sceneKey": "light_enrichment_additional_real_sample",
|
||||
"batchRole": "second-expansion-anchor",
|
||||
"status": "promoted-expansion"
|
||||
}
|
||||
],
|
||||
"notes": [
|
||||
"This batch records the current G1-E representative and promoted expansion baselines.",
|
||||
"The additional sample is now promoted through the same light-enrichment family contract.",
|
||||
"This keeps G1-E within the roadmap Track B and Track D boundary without forcing a new family scope."
|
||||
]
|
||||
}
|
||||
136
tests/fixtures/generated_scene/g1e_light_enrichment/index.html
vendored
Normal file
136
tests/fixtures/generated_scene/g1e_light_enrichment/index.html
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="sgclaw-scene-kind" content="report_collection" />
|
||||
<meta name="sgclaw-target-url" content="http://yx.gs.sgcc.com.cn/" />
|
||||
<meta name="sgclaw-expected-domain" content="yx.gs.sgcc.com.cn" />
|
||||
<title>高低压新增报装容量月度统计表</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const workUrl =
|
||||
"http://yxgateway.gs.sgcc.com.cn/emss-cmnf-common-front/member/workOrderQuery/getWkorderAll";
|
||||
const headers = { "Content-Type": "application/json" };
|
||||
|
||||
async function report() {
|
||||
const mode = "month";
|
||||
const month = "04";
|
||||
const reportType = "capacity";
|
||||
const groupedData = {};
|
||||
const requests = [];
|
||||
requests.push(
|
||||
axios.post(
|
||||
workUrl,
|
||||
window.encrypt_old({
|
||||
wkOrderNo: "",
|
||||
pageNo: 1,
|
||||
pageSize: 1000
|
||||
}),
|
||||
{ headers }
|
||||
)
|
||||
);
|
||||
|
||||
const queryElectCustInfoApi = async (data) => {
|
||||
const url =
|
||||
"http://yxgateway.gs.sgcc.com.cn/emss-busexpengaccsf-busexpaccssrv-front/member/workorder/queryElectCustInfo";
|
||||
const response = await axios.post(
|
||||
url,
|
||||
window.encrypt_old({
|
||||
appNo: data.wkOrderNo
|
||||
}),
|
||||
{ headers }
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
const queryBusAcptApi = async (data) => {
|
||||
const url =
|
||||
"http://yxgateway.gs.sgcc.com.cn/emss-busexpengaccsf-busexpaccssrvacc-front/member/commonBusAcpt97/queryBusAcpt";
|
||||
const response = await axios.post(
|
||||
url,
|
||||
window.encrypt_old({
|
||||
appNo: data.wkOrderNo
|
||||
}),
|
||||
{ headers }
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
const getBatchPerCust97Api = async (data) => {
|
||||
const url =
|
||||
"http://yxgateway.gs.sgcc.com.cn/emss-busexpengaccsf-busexpaccssrvacc-front/member/lvbatchnewinstBusAcpt97/getBatchPerCust97";
|
||||
const response = await axios.post(
|
||||
url,
|
||||
window.encrypt_old({
|
||||
pageNo: 1,
|
||||
pageSize: 20,
|
||||
baseNewFlag: "01",
|
||||
appNo: data.wkOrderNo
|
||||
}),
|
||||
{ headers }
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
for (const item of combinedData) {
|
||||
const { countyCodeName } = item;
|
||||
if (!groupedData[countyCodeName]) {
|
||||
groupedData[countyCodeName] = {
|
||||
countyCodeName,
|
||||
hightPressureTotalThisMonth: 0,
|
||||
hightPressureTotalOtherMonth: 0,
|
||||
hightVolTotalThisMonth: 0,
|
||||
hightVolTotalOtherMonth: 0,
|
||||
lowPressureTotalThisMonth: 0,
|
||||
lowPressureTotalOtherMonth: 0,
|
||||
lowVolTotalThisMonth: 0,
|
||||
lowVolTotalOtherMonth: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
for (const item of Object.values(groupedData)) {
|
||||
const {
|
||||
countyCodeName,
|
||||
hightAppListThisMonth,
|
||||
lowAppListThisMonth,
|
||||
batchAppListThisMonth
|
||||
} = item;
|
||||
const hightThis = await this.asyncPool(20, hightAppListThisMonth, queryElectCustInfoApi);
|
||||
const lowThis = await this.asyncPool(20, lowAppListThisMonth[1], queryElectCustInfoApi);
|
||||
const volBatchThis = await this.asyncPool(20, lowAppListThisMonth[0], queryBusAcptApi);
|
||||
const preBatchThis = await this.asyncPool(20, batchAppListThisMonth, getBatchPerCust97Api);
|
||||
if (hightThis && hightThis.status === 200 && mode === "month" && reportType) {
|
||||
groupedData[countyCodeName].month = month;
|
||||
}
|
||||
await com(hightThis, countyCodeName, "hightVolTotalThisMonth", "hightPressureTotalThisMonth");
|
||||
await com(lowThis, countyCodeName, "lowVolTotalThisMonth", "lowPressureTotalThisMonth");
|
||||
await batchCom(volBatchThis, preBatchThis, countyCodeName, "lowVolTotalThisMonth", "lowPressureTotalThisMonth");
|
||||
}
|
||||
|
||||
const titleList = [
|
||||
["index", "序号", ""],
|
||||
["countyCodeName", "单位", ""],
|
||||
["hightPressureTotalOtherMonth", "月前累计新增接入", "高压户数"],
|
||||
["hightVolTotalOtherMonth", "月前累计新增接入", "高压容量"],
|
||||
["lowPressureTotalOtherMonth", "月前累计新增接入", "低压户数"],
|
||||
["lowVolTotalOtherMonth", "月前累计新增接入", "低压容量"],
|
||||
["otherMonthShare", "月前累计新增接入", "容量占比"],
|
||||
["hightPressureTotalThisMonth", "本月新增接入", "高压户数"],
|
||||
["hightVolTotalThisMonth", "本月新增接入", "高压容量"],
|
||||
["lowPressureTotalThisMonth", "本月新增接入", "低压户数"],
|
||||
["lowVolTotalThisMonth", "本月新增接入", "低压容量"],
|
||||
["thisMonthShare", "本月新增接入", "容量占比"],
|
||||
["yearHightPressureTotal", "年累计新增接入", "高压户数"],
|
||||
["yearHightVolTotal", "年累计新增接入", "高压容量"],
|
||||
["yearLowPressureTotal", "年累计新增接入", "低压户数"],
|
||||
["yearLowVolTotal", "年累计新增接入", "低压容量"],
|
||||
["yearShare", "年累计新增接入", "容量占比"]
|
||||
];
|
||||
|
||||
return { groupedData, titleList };
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
100
tests/fixtures/generated_scene/g1e_light_enrichment_additional/index.html
vendored
Normal file
100
tests/fixtures/generated_scene/g1e_light_enrichment_additional/index.html
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="sgclaw-scene-kind" content="report_collection" />
|
||||
<meta name="sgclaw-target-url" content="http://yx.gs.sgcc.com.cn/" />
|
||||
<meta name="sgclaw-expected-domain" content="yx.gs.sgcc.com.cn" />
|
||||
<title>G1-E Additional Light Enrichment Fixture</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const sourceUrl = "http://yx.gs.sgcc.com.cn/report";
|
||||
const headers = { "Content-Type": "application/json" };
|
||||
const workUrl =
|
||||
"http://yx.gs.sgcc.com.cn/report/customerApply/getWkorderAll";
|
||||
|
||||
async function reportAdditional() {
|
||||
const mode = "month";
|
||||
const month = "04";
|
||||
const reportType = "capacity";
|
||||
const groupedData = {};
|
||||
|
||||
const mainResponse = await axios.post(
|
||||
workUrl,
|
||||
window.encrypt_old({
|
||||
wkOrderNo: "",
|
||||
countyCodeName: "",
|
||||
pageNo: 1,
|
||||
pageSize: 500
|
||||
}),
|
||||
{ headers }
|
||||
);
|
||||
|
||||
const queryBusAcptApi = async (data) => {
|
||||
const url =
|
||||
"http://yx.gs.sgcc.com.cn/report/customerApply/queryBusAcpt";
|
||||
const response = await axios.post(
|
||||
url,
|
||||
window.encrypt_old({
|
||||
appNo: data.wkOrderNo,
|
||||
countyCodeName: data.countyCodeName
|
||||
}),
|
||||
{ headers }
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
const queryCapacityInfoApi = async (data) => {
|
||||
const url =
|
||||
"http://yx.gs.sgcc.com.cn/report/customerApply/queryCapacityInfo";
|
||||
const response = await axios.post(
|
||||
url,
|
||||
window.encrypt_old({
|
||||
appNo: data.wkOrderNo
|
||||
}),
|
||||
{ headers }
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
const rows = mainResponse.data || [];
|
||||
for (const item of rows) {
|
||||
const { countyCodeName } = item;
|
||||
if (!groupedData[countyCodeName]) {
|
||||
groupedData[countyCodeName] = {
|
||||
countyCodeName,
|
||||
batchTotalThisMonth: 0,
|
||||
batchCapacityThisMonth: 0,
|
||||
customerTotalThisMonth: 0,
|
||||
customerCapacityThisMonth: 0,
|
||||
thisMonthShare: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
for (const item of rows) {
|
||||
const busAcpt = await queryBusAcptApi(item);
|
||||
const capacityInfo = await queryCapacityInfoApi(item);
|
||||
if (busAcpt && busAcpt.status === 200 && mode === "month" && reportType) {
|
||||
groupedData[item.countyCodeName].month = month;
|
||||
}
|
||||
await com(busAcpt, item.countyCodeName, "batchCapacityThisMonth", "batchTotalThisMonth");
|
||||
await com(capacityInfo, item.countyCodeName, "customerCapacityThisMonth", "customerTotalThisMonth");
|
||||
}
|
||||
|
||||
const titleList = [
|
||||
["index", "Index", ""],
|
||||
["countyCodeName", "Org", ""],
|
||||
["batchTotalThisMonth", "This Month", "Batch Count"],
|
||||
["batchCapacityThisMonth", "This Month", "Batch Capacity"],
|
||||
["customerTotalThisMonth", "This Month", "Customer Count"],
|
||||
["customerCapacityThisMonth", "This Month", "Customer Capacity"],
|
||||
["thisMonthShare", "This Month", "Capacity Share"]
|
||||
];
|
||||
|
||||
return { groupedData, titleList };
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
100
tests/fixtures/generated_scene/g1e_light_enrichment_expansion/index.html
vendored
Normal file
100
tests/fixtures/generated_scene/g1e_light_enrichment_expansion/index.html
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="sgclaw-scene-kind" content="report_collection" />
|
||||
<meta name="sgclaw-target-url" content="http://yx.gs.sgcc.com.cn/" />
|
||||
<meta name="sgclaw-expected-domain" content="yx.gs.sgcc.com.cn" />
|
||||
<title>Light Enrichment Expansion Fixture</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const sourceUrl = "http://yx.gs.sgcc.com.cn/report";
|
||||
const headers = { "Content-Type": "application/json" };
|
||||
const workUrl =
|
||||
"http://yx.gs.sgcc.com.cn/report/customerApply/getWkorderAll";
|
||||
|
||||
async function reportExpansion() {
|
||||
const mode = "month";
|
||||
const month = "04";
|
||||
const reportType = "asset";
|
||||
const groupedData = {};
|
||||
|
||||
const mainResponse = await axios.post(
|
||||
workUrl,
|
||||
window.encrypt_old({
|
||||
wkOrderNo: "",
|
||||
countyCodeName: "",
|
||||
pageNo: 1,
|
||||
pageSize: 500
|
||||
}),
|
||||
{ headers }
|
||||
);
|
||||
|
||||
const queryMeterInfoApi = async (data) => {
|
||||
const url =
|
||||
"http://yx.gscc.com.cn/report/customerApply/queryMeterInfo";
|
||||
const response = await axios.post(
|
||||
url,
|
||||
window.encrypt_old({
|
||||
appNo: data.wkOrderNo,
|
||||
countyCodeName: data.countyCodeName
|
||||
}),
|
||||
{ headers }
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
const queryTransformerInfoApi = async (item) => {
|
||||
const url =
|
||||
"http://yx.gscc.com.cn/report/customerApply/queryTransformerInfo";
|
||||
const response = await axios.post(
|
||||
url,
|
||||
window.encrypt_old({
|
||||
appNo: item.wkOrderNo
|
||||
}),
|
||||
{ headers }
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
const rows = mainResponse.data || [];
|
||||
for (const item of rows) {
|
||||
const { countyCodeName } = item;
|
||||
if (!groupedData[countyCodeName]) {
|
||||
groupedData[countyCodeName] = {
|
||||
countyCodeName,
|
||||
meterTotalThisMonth: 0,
|
||||
meterCapacityThisMonth: 0,
|
||||
transformerTotalThisMonth: 0,
|
||||
transformerCapacityThisMonth: 0,
|
||||
thisMonthShare: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
for (const item of rows) {
|
||||
const meterInfo = await queryMeterInfoApi(item);
|
||||
const transformerInfo = await queryTransformerInfoApi(item);
|
||||
if (meterInfo && meterInfo.status === 200 && mode === "month" && reportType) {
|
||||
groupedData[item.countyCodeName].month = month;
|
||||
}
|
||||
await com(meterInfo, item.countyCodeName, "meterCapacityThisMonth", "meterTotalThisMonth");
|
||||
await com(transformerInfo, item.countyCodeName, "transformerCapacityThisMonth", "transformerTotalThisMonth");
|
||||
}
|
||||
|
||||
const titleList = [
|
||||
["index", "Index", ""],
|
||||
["countyCodeName", "Org", ""],
|
||||
["meterTotalThisMonth", "This Month", "Meter Count"],
|
||||
["meterCapacityThisMonth", "This Month", "Meter Capacity"],
|
||||
["transformerTotalThisMonth", "This Month", "Transformer Count"],
|
||||
["transformerCapacityThisMonth", "This Month", "Transformer Capacity"],
|
||||
["thisMonthShare", "This Month", "Capacity Share"]
|
||||
];
|
||||
|
||||
return { groupedData, titleList };
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
53
tests/fixtures/generated_scene/g1e_remaining_fail_closed_closure_followup_2026-04-19.json
vendored
Normal file
53
tests/fixtures/generated_scene/g1e_remaining_fail_closed_closure_followup_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"runDate": "2026-04-19",
|
||||
"parentFramework": "2026-04-19-scene-skill-102-full-coverage-framework-plan",
|
||||
"parentRoute": "Route 4 / G1-E single_request_enrichment closure",
|
||||
"plan": "2026-04-19-g1e-remaining-fail-closed-closure-plan.md",
|
||||
"scope": {
|
||||
"fixedInputBucket": "single_request_enrichment structured fail-closed",
|
||||
"baselineCount": 2,
|
||||
"allowedChange": "recover G1-E output column mappings from cols.push and wide titleList declarations",
|
||||
"forbiddenChanges": [
|
||||
"execution board update",
|
||||
"new family",
|
||||
"Route 2/3 asset changes",
|
||||
"Route 5+ work"
|
||||
]
|
||||
},
|
||||
"summary": {
|
||||
"beforeFailClosed": 2,
|
||||
"resolvedToAutoPass": 2,
|
||||
"remainingFailClosed": 0,
|
||||
"coverageDelta": 2
|
||||
},
|
||||
"scenes": [
|
||||
{
|
||||
"sceneId": "sweep-013-scene",
|
||||
"sceneName": "业扩报装质量评价体系",
|
||||
"baselineStatus": "fail-closed-known",
|
||||
"baselineReadiness": "B",
|
||||
"baselineBlockers": [
|
||||
"output_columns"
|
||||
],
|
||||
"followupStatus": "auto-pass",
|
||||
"followupReadiness": "A",
|
||||
"archetype": "single_request_enrichment",
|
||||
"resolvedBy": "g1e_output_columns_from_cols_push"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-068-scene",
|
||||
"sceneName": "用电报装信息统计列表",
|
||||
"baselineStatus": "fail-closed-known",
|
||||
"baselineReadiness": "B",
|
||||
"baselineBlockers": [
|
||||
"output_columns"
|
||||
],
|
||||
"followupStatus": "auto-pass",
|
||||
"followupReadiness": "A",
|
||||
"archetype": "single_request_enrichment",
|
||||
"resolvedBy": "g1e_output_columns_from_wide_title_list"
|
||||
}
|
||||
],
|
||||
"outputRoot": "examples/g1e_remaining_fail_closed_closure_followup_2026-04-19",
|
||||
"stopStatement": "Route 4 is closed after measuring the fixed G1-E bucket delta. Do not begin Route 5 in this plan."
|
||||
}
|
||||
110
tests/fixtures/generated_scene/g2_candidate_batch_2026-04-18.json
vendored
Normal file
110
tests/fixtures/generated_scene/g2_candidate_batch_2026-04-18.json
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
{
|
||||
"batchId": "g2-lineloss-family-candidates-2026-04-18",
|
||||
"family": "G2",
|
||||
"source": "docs/superpowers/reports/2026-04-18-g2-family-expansion-third-round-report.md",
|
||||
"supportingSources": [
|
||||
"docs/superpowers/reports/2026-04-18-lineloss-family-variant-expansion-report.md",
|
||||
"tests/fixtures/generated_scene/scene_ledger_status_2026-04-18.json"
|
||||
],
|
||||
"ledgerClusterLabel": "lineloss-family-candidate",
|
||||
"selectionRule": "roadmap Track A and Track D line-loss family variants promoted into reusable multi-mode baselines",
|
||||
"candidateCount": 6,
|
||||
"representativeBaseline": "tests/fixtures/generated_scene/multi_mode",
|
||||
"promotedBatchExpansionBaselines": [
|
||||
{
|
||||
"fixtureDir": "tests/fixtures/generated_scene/g2_weekly_single_mode",
|
||||
"sceneId": "p1-g2-weekly-single-mode-report",
|
||||
"sceneName": "P1 G2 weekly single mode report",
|
||||
"assertions": {
|
||||
"requiredDefaultMode": "week"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fixtureDir": "tests/fixtures/generated_scene/g2_mixed_linked_workflow",
|
||||
"sceneId": "p1-g2-mixed-linked-workflow-report",
|
||||
"sceneName": "P1 G2 mixed linked workflow report",
|
||||
"assertions": {
|
||||
"requiredDefaultMode": "primary"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fixtureDir": "tests/fixtures/generated_scene/g2_comparison_crosscheck",
|
||||
"sceneId": "p1-g2-comparison-crosscheck-report",
|
||||
"sceneName": "P1 G2 comparison crosscheck report",
|
||||
"assertions": {
|
||||
"requiredDefaultMode": "comparison"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fixtureDir": "tests/fixtures/generated_scene/g2_diagnosis_drilldown",
|
||||
"sceneId": "p1-g2-diagnosis-drilldown-report",
|
||||
"sceneName": "P1 G2 diagnosis drilldown report",
|
||||
"assertions": {
|
||||
"requiredDefaultMode": "diagnosis"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fixtureDir": "tests/fixtures/generated_scene/g2_prediction_compute",
|
||||
"sceneId": "p1-g2-prediction-compute-report",
|
||||
"sceneName": "P1 G2 prediction compute report",
|
||||
"assertions": {
|
||||
"requiredDefaultMode": "prediction"
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectedSharedContract": {
|
||||
"archetype": "multi_mode_request",
|
||||
"requiredGateNames": [
|
||||
"bootstrap_resolved",
|
||||
"request_contract_complete",
|
||||
"response_contract_complete",
|
||||
"workflow_contract_complete",
|
||||
"runtime_contract_compatible"
|
||||
],
|
||||
"requiredVariantPatterns": [
|
||||
"g2_a_dual_mode_baseline",
|
||||
"g2_b_weekly_single_mode",
|
||||
"g2_c_mixed_linked_workflow",
|
||||
"g2_d_prediction_compute",
|
||||
"g2_e_comparison_crosscheck",
|
||||
"g2_f_diagnosis_drilldown"
|
||||
]
|
||||
},
|
||||
"candidates": [
|
||||
{
|
||||
"sceneKey": "tq_lineloss_report",
|
||||
"batchRole": "p0-anchor",
|
||||
"status": "promoted-baseline"
|
||||
},
|
||||
{
|
||||
"sceneKey": "baiyin_lineloss_weekly",
|
||||
"batchRole": "first-expansion-anchor",
|
||||
"status": "promoted-expansion"
|
||||
},
|
||||
{
|
||||
"sceneKey": "lineloss_period_diff",
|
||||
"batchRole": "second-expansion-anchor",
|
||||
"status": "promoted-expansion"
|
||||
},
|
||||
{
|
||||
"sceneKey": "zero_consumer_crosscheck",
|
||||
"batchRole": "third-expansion-anchor",
|
||||
"status": "promoted-expansion"
|
||||
},
|
||||
{
|
||||
"sceneKey": "steal_analysis",
|
||||
"batchRole": "fourth-expansion-anchor",
|
||||
"status": "promoted-expansion"
|
||||
},
|
||||
{
|
||||
"sceneKey": "predicted_compute_variant",
|
||||
"batchRole": "fifth-expansion-anchor",
|
||||
"status": "promoted-expansion"
|
||||
}
|
||||
],
|
||||
"notes": [
|
||||
"This batch records the current code-backed G2 representative and promoted expansion baselines.",
|
||||
"G2-D prediction compute is now promoted as a repo-local reusable expansion baseline within the current lineloss family contract.",
|
||||
"The next Track D round should continue from the queued downstream family candidates instead of rebuilding G2-A through G2-F state from markdown reports."
|
||||
]
|
||||
}
|
||||
34
tests/fixtures/generated_scene/g2_comparison_crosscheck/index.html
vendored
Normal file
34
tests/fixtures/generated_scene/g2_comparison_crosscheck/index.html
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>G2 Comparison Crosscheck Fixture</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const sourceUrl = "http://20.76.57.61:18080/gsllys";
|
||||
const reportType = "zeroConsumerCrosscheck";
|
||||
|
||||
async function queryMainRank(orgno, fdate) {
|
||||
return $.ajax({
|
||||
url: "http://20.76.57.61:18080/gsllys/tqLinelossStatis/getTqLinelossInfoListRank",
|
||||
type: "POST",
|
||||
contentType: "application/x-www-form-urlencoded",
|
||||
data: JSON.stringify({ orgno, fdate, page: 1, rows: 40 })
|
||||
});
|
||||
}
|
||||
|
||||
async function queryUserElectric(orgno, consno, fdate) {
|
||||
return $.ajax({
|
||||
url: "http://20.76.57.61:18080/gsllys/tqLinelossStatis/getUserElectricList",
|
||||
type: "POST",
|
||||
contentType: "application/x-www-form-urlencoded",
|
||||
data: JSON.stringify({ orgno, consno, fdate, page: 1, rows: 20 })
|
||||
});
|
||||
}
|
||||
|
||||
function mergeRow(TG_NO, TG_NAME, consno, userNmae, thisMonth, beforeMonth1) {
|
||||
return { TG_NO, TG_NAME, consno, userNmae, thisMonth, beforeMonth1 };
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
52
tests/fixtures/generated_scene/g2_diagnosis_drilldown/index.html
vendored
Normal file
52
tests/fixtures/generated_scene/g2_diagnosis_drilldown/index.html
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>G2 Diagnosis Drilldown Fixture</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const sourceUrl = "http://20.76.57.61:18080/gsllys";
|
||||
const reportType = "stealAnalysis";
|
||||
|
||||
async function queryMainRank(orgno, fdate) {
|
||||
return $.ajax({
|
||||
url: "http://20.76.57.61:18080/gsllys/tqLinelossStatis/getTqLinelossInfoListRank",
|
||||
type: "POST",
|
||||
contentType: "application/x-www-form-urlencoded",
|
||||
data: JSON.stringify({ orgno, fdate, page: 1, rows: 100 })
|
||||
});
|
||||
}
|
||||
|
||||
async function queryDiagnose(orgno, tgNo, fdate) {
|
||||
return $.ajax({
|
||||
url: "http://20.76.57.61:18080/gsllys/tqLinelossStatis/tqAutoDiagnoseAnalyse/search",
|
||||
type: "POST",
|
||||
contentType: "application/x-www-form-urlencoded",
|
||||
data: JSON.stringify({ orgno, tgNo, fdate })
|
||||
});
|
||||
}
|
||||
|
||||
async function queryDetail(orgno, tgNo, fdate) {
|
||||
return $.ajax({
|
||||
url: "http://20.76.57.61:18080/gsllys/stealElecAnalyse/getFlqdyhDetailList",
|
||||
type: "POST",
|
||||
contentType: "application/x-www-form-urlencoded",
|
||||
data: JSON.stringify({ orgno, tgNo, fdate, page: 1, rows: 20 })
|
||||
});
|
||||
}
|
||||
|
||||
async function queryRemark(orgno, tgNo, fdate) {
|
||||
return $.ajax({
|
||||
url: "http://20.76.57.61:18080/gsllys/stealElecAnalyse/userVoltsAndElecflowMoniter/search",
|
||||
type: "POST",
|
||||
contentType: "application/x-www-form-urlencoded",
|
||||
data: JSON.stringify({ orgno, tgNo, fdate, page: 1, rows: 20 })
|
||||
});
|
||||
}
|
||||
|
||||
function exportRow(TG_NO, LL_TYPE_NAME, LOSS_PQ, LINELOSS_RATE, remark) {
|
||||
return { TG_NO, LL_TYPE_NAME, LOSS_PQ, LINELOSS_RATE, remark };
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
50
tests/fixtures/generated_scene/g2_mixed_linked_workflow/index.html
vendored
Normal file
50
tests/fixtures/generated_scene/g2_mixed_linked_workflow/index.html
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>G2 Mixed Linked Workflow Fixture</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const sourceUrl = "http://20.76.57.61:18080/gsllys";
|
||||
|
||||
async function queryMainRank(formDate) {
|
||||
return $.ajax({
|
||||
url: "http://20.76.57.61:18080/gsllys/tqLinelossStatis/getTqLinelossInfoListRank",
|
||||
type: "POST",
|
||||
contentType: "application/x-www-form-urlencoded",
|
||||
data: JSON.stringify(formDate)
|
||||
});
|
||||
}
|
||||
|
||||
async function queryUserElectric(query) {
|
||||
return $.ajax({
|
||||
url: "http://20.76.57.61:18080/gsllys/tqLinelossStatis/getUserElectricList",
|
||||
type: "POST",
|
||||
contentType: "application/x-www-form-urlencoded",
|
||||
data: JSON.stringify(query)
|
||||
});
|
||||
}
|
||||
|
||||
async function queryPeerSystemToken(dlMesage) {
|
||||
return $.ajax({
|
||||
url: "http://10.4.39.180/xsgl/syncLineLoss/rest/syncLineLossService/workbench",
|
||||
type: "POST",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
data: JSON.stringify({
|
||||
userName: dlMesage.userName,
|
||||
loginName: dlMesage.loginName,
|
||||
urlType: "WORKBENCH",
|
||||
url: "/loginInfo/getToken"
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function bootScene() {
|
||||
return sgBrowserExcuteJsCode(
|
||||
"http://20.76.57.61:18080/gsllys/tqLinelossStatis/tqQualifyRateMonitor",
|
||||
"run"
|
||||
);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
112
tests/fixtures/generated_scene/g2_noisy_multi_mode/index.html
vendored
Normal file
112
tests/fixtures/generated_scene/g2_noisy_multi_mode/index.html
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>G2 Noisy Multi Mode Fixture</title>
|
||||
</head>
|
||||
<body>
|
||||
<script src="/a_js/jquery.js"></script>
|
||||
<script>
|
||||
const loginPath = "http://20.77.115.36:31051";
|
||||
const mainPath = "http://20.77.115.36:31051";
|
||||
const sourceUrl = "http://20.76.57.61:18080/gsllys";
|
||||
const reportType = "lineloss";
|
||||
|
||||
async function queryLineloss(period_mode, orgno, weekSfdate, weekEfdate) {
|
||||
if (period_mode === "month") {
|
||||
const datas = {
|
||||
orgno,
|
||||
yn_flag: 0,
|
||||
rows: 20,
|
||||
page: 1,
|
||||
sidx: "TO_NUMBER(ORG_NO)",
|
||||
sord: "asc",
|
||||
fdate: moment().subtract(1, "months").format("YYYY-MM"),
|
||||
tjzq: "month"
|
||||
};
|
||||
return $.ajax({
|
||||
url: "http://20.76.57.61:18080/gsllys/fourVerEightHor/fourVerEightHorLinelossRateList",
|
||||
type: "POST",
|
||||
contentType: "application/x-www-form-urlencoded",
|
||||
data: JSON.stringify(datas)
|
||||
});
|
||||
}
|
||||
if (period_mode === "week") {
|
||||
const datas = {
|
||||
orgno,
|
||||
tjzq: "week",
|
||||
rows: 20,
|
||||
page: 1,
|
||||
sord: "asc",
|
||||
weekSfdate,
|
||||
weekEfdate
|
||||
};
|
||||
return $.ajax({
|
||||
url: "http://20.76.57.61:18080/gsllys/tqLinelossStatis/getYearMonWeekLinelossAnalysisList",
|
||||
type: "POST",
|
||||
contentType: "application/x-www-form-urlencoded",
|
||||
data: JSON.stringify(datas)
|
||||
});
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
const cols1 = [
|
||||
["ORG_NAME", "供电单位"],
|
||||
["LINE_LOSS_RATE", "综合线损率(%)"],
|
||||
["PPQ", "供电量(Kwh)"],
|
||||
["UPQ", "售电量(Kwh)"],
|
||||
["LOSS_PQ", "损失电量(Kwh)"]
|
||||
]
|
||||
|
||||
const cols2 = [
|
||||
["ORG_NAME", "供电单位"],
|
||||
["YGDL", "累计供电量"],
|
||||
["YYDL", "累计售电量"],
|
||||
["YXSL", "线损完成率(%)"],
|
||||
["RAT_SCOPE", "线损率累计目标值"],
|
||||
["BLANK3", "目标完成率"],
|
||||
["BLANK2", "排名"]
|
||||
];
|
||||
|
||||
async function getUserInfo(custNo) {
|
||||
return $.ajax({
|
||||
url: "http://yxgateway.gs.sgcc.com.cn/emss-custmgtf-custview-front/member/cust360ViewInfo/getCustElecInfoByCustIdUnion",
|
||||
type: "POST",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({ custNo })
|
||||
});
|
||||
}
|
||||
|
||||
async function executeTask(period_mode, orgno) {
|
||||
const pageSize = 20;
|
||||
let page = 1;
|
||||
const rows = [];
|
||||
while (page < 5) {
|
||||
const response = await queryLineloss(period_mode, orgno, "2026-03-01", "2026-03-07");
|
||||
const list = response.content || [];
|
||||
if (!list.length) break;
|
||||
for (const item of list) {
|
||||
if (item.statusName !== "回退营销" && item.loss !== null) {
|
||||
rows.push(item);
|
||||
}
|
||||
}
|
||||
page += 1;
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
function exportExcel(rows) {
|
||||
return $.ajax({
|
||||
url: "http://localhost:13313/SurfaceServices/personalBread/export/faultDetailsExportXLSX",
|
||||
type: "POST",
|
||||
data: rows
|
||||
});
|
||||
}
|
||||
|
||||
const docs1 = "https://github.com/nodeca/pako/";
|
||||
const docs2 = "https://developer.mozilla.org/En/XMLHttpRequest/Using_XMLHttpRequest#Receiving_binary_data_in_older_browsers";
|
||||
const docs3 = "https://stackoverflow.com/q/3277182/1008999";
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
39
tests/fixtures/generated_scene/g2_prediction_compute/index.html
vendored
Normal file
39
tests/fixtures/generated_scene/g2_prediction_compute/index.html
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>G2 Prediction Compute Fixture</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const sourceUrl = "http://20.76.57.61:18080/gsllys";
|
||||
const reportType = "predictionCompute";
|
||||
const period_mode = "prediction";
|
||||
|
||||
async function queryHighLossForecast(orgno, fdate) {
|
||||
const params = {
|
||||
orgno,
|
||||
fdate,
|
||||
tjzq: "month",
|
||||
mode: "prediction",
|
||||
page: 1,
|
||||
rows: 60,
|
||||
reportType: "predictionCompute"
|
||||
};
|
||||
return $.ajax({
|
||||
url: "http://20.76.57.61:18080/gsllys/tqLinelossStatis/highLineLossForecast",
|
||||
type: "POST",
|
||||
contentType: "application/x-www-form-urlencoded",
|
||||
data: JSON.stringify(params)
|
||||
});
|
||||
}
|
||||
|
||||
function runPredictionMode() {
|
||||
return queryHighLossForecast("6201001", "2026-04");
|
||||
}
|
||||
|
||||
function normalizePredictionRow(lineId, lineName, lineLossType, lineLossRate, powerLoss) {
|
||||
return { lineId, lineName, lineLossType, lineLossRate, powerLoss };
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
83
tests/fixtures/generated_scene/g2_remaining_fail_closed_closure_followup_2026-04-19.json
vendored
Normal file
83
tests/fixtures/generated_scene/g2_remaining_fail_closed_closure_followup_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"runDate": "2026-04-19",
|
||||
"parentFramework": "2026-04-19-scene-skill-102-full-coverage-framework-plan",
|
||||
"parentRoute": "Route 3 / G2 multi_mode_request closure",
|
||||
"plan": "2026-04-19-g2-remaining-fail-closed-closure-plan.md",
|
||||
"scope": {
|
||||
"fixedInputBucket": "multi_mode_request structured fail-closed",
|
||||
"baselineCount": 4,
|
||||
"allowedChange": "fill empty G2 mode requestTemplate from bounded mode-specific fallback",
|
||||
"forbiddenChanges": [
|
||||
"execution board update",
|
||||
"new family",
|
||||
"Route 2 asset changes",
|
||||
"Route 4+ work"
|
||||
]
|
||||
},
|
||||
"summary": {
|
||||
"beforeFailClosed": 4,
|
||||
"resolvedToAutoPass": 4,
|
||||
"remainingFailClosed": 0,
|
||||
"coverageDelta": 4
|
||||
},
|
||||
"scenes": [
|
||||
{
|
||||
"sceneId": "sweep-020-scene",
|
||||
"sceneName": "供电所线路电量统计",
|
||||
"baselineStatus": "fail-closed-known",
|
||||
"baselineReadiness": "C",
|
||||
"baselineBlockers": [
|
||||
"g2_request_contract",
|
||||
"request_mode_contract"
|
||||
],
|
||||
"followupStatus": "auto-pass",
|
||||
"followupReadiness": "A",
|
||||
"archetype": "multi_mode_request",
|
||||
"resolvedBy": "g2_mode_request_template_fallback"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-023-scene",
|
||||
"sceneName": "供电质量看板-武威",
|
||||
"baselineStatus": "fail-closed-known",
|
||||
"baselineReadiness": "C",
|
||||
"baselineBlockers": [
|
||||
"g2_request_contract",
|
||||
"request_mode_contract"
|
||||
],
|
||||
"followupStatus": "auto-pass",
|
||||
"followupReadiness": "A",
|
||||
"archetype": "multi_mode_request",
|
||||
"resolvedBy": "g2_mode_request_template_fallback"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-070-scene",
|
||||
"sceneName": "电量、站损自动采集上报",
|
||||
"baselineStatus": "fail-closed-known",
|
||||
"baselineReadiness": "C",
|
||||
"baselineBlockers": [
|
||||
"g2_request_contract",
|
||||
"request_mode_contract"
|
||||
],
|
||||
"followupStatus": "auto-pass",
|
||||
"followupReadiness": "A",
|
||||
"archetype": "multi_mode_request",
|
||||
"resolvedBy": "g2_mode_request_template_fallback"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-083-scene",
|
||||
"sceneName": "营销业务管控监测日报表",
|
||||
"baselineStatus": "fail-closed-known",
|
||||
"baselineReadiness": "C",
|
||||
"baselineBlockers": [
|
||||
"g2_request_contract",
|
||||
"request_mode_contract"
|
||||
],
|
||||
"followupStatus": "auto-pass",
|
||||
"followupReadiness": "A",
|
||||
"archetype": "multi_mode_request",
|
||||
"resolvedBy": "g2_mode_request_template_fallback"
|
||||
}
|
||||
],
|
||||
"outputRoot": "examples/g2_remaining_fail_closed_closure_followup_2026-04-19",
|
||||
"stopStatement": "Route 3 is closed after measuring the fixed G2 bucket delta. Do not begin Route 4 in this plan."
|
||||
}
|
||||
49
tests/fixtures/generated_scene/g2_residual_2_readiness_closure_followup_2026-04-19.json
vendored
Normal file
49
tests/fixtures/generated_scene/g2_residual_2_readiness_closure_followup_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"runDate": "2026-04-19",
|
||||
"parentPlan": "docs/superpowers/plans/2026-04-19-g2-residual-2-readiness-closure-plan.md",
|
||||
"outputRoot": "D:\\data\\ideaSpace\\rust\\sgClaw\\claw-new\\examples\\g2_residual_2_readiness_closure_2026-04-19",
|
||||
"summary": {
|
||||
"total": 2,
|
||||
"pass": 2,
|
||||
"failClosed": 0,
|
||||
"sourceUnreadable": 0,
|
||||
"unknown": 0
|
||||
},
|
||||
"scenes": [
|
||||
{
|
||||
"sceneId": "sweep-018-scene",
|
||||
"sceneName": "白银线损周报",
|
||||
"exitCode": 0,
|
||||
"durationSeconds": 46.4,
|
||||
"workflowArchetype": "multi_mode_request",
|
||||
"readinessLevel": "A",
|
||||
"generationStatus": "",
|
||||
"routeStatus": "pass",
|
||||
"modeNames": [
|
||||
"diagnosis"
|
||||
],
|
||||
"g2ModesPresentPassed": true,
|
||||
"g2RequestContractCompletePassed": true,
|
||||
"g2ResponseContractCompletePassed": true,
|
||||
"reportPath": "D:\\data\\ideaSpace\\rust\\sgClaw\\claw-new\\examples\\g2_residual_2_readiness_closure_2026-04-19\\skills\\sweep-018-scene\\references\\generation-report.json"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-071-scene",
|
||||
"sceneName": "台区线损大数据-月_周累计线损率统计分析",
|
||||
"exitCode": 0,
|
||||
"durationSeconds": 31.28,
|
||||
"workflowArchetype": "multi_mode_request",
|
||||
"readinessLevel": "A",
|
||||
"generationStatus": "",
|
||||
"routeStatus": "pass",
|
||||
"modeNames": [
|
||||
"month",
|
||||
"week"
|
||||
],
|
||||
"g2ModesPresentPassed": true,
|
||||
"g2RequestContractCompletePassed": true,
|
||||
"g2ResponseContractCompletePassed": true,
|
||||
"reportPath": "D:\\data\\ideaSpace\\rust\\sgClaw\\claw-new\\examples\\g2_residual_2_readiness_closure_2026-04-19\\skills\\sweep-071-scene\\references\\generation-report.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
42
tests/fixtures/generated_scene/g2_weekly_single_mode/index.html
vendored
Normal file
42
tests/fixtures/generated_scene/g2_weekly_single_mode/index.html
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>G2 Weekly Single Mode Fixture</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const sourceUrl = "http://20.76.57.61:18080/gsllys";
|
||||
|
||||
async function queryWeeklyLineloss(orgno, weekSfdate, weekEfdate) {
|
||||
const params = {
|
||||
orgno,
|
||||
tjzq: "week",
|
||||
level: "02",
|
||||
rows: 20,
|
||||
page: 1,
|
||||
weekSfdate,
|
||||
weekEfdate
|
||||
};
|
||||
return $.ajax({
|
||||
url: "http://20.76.57.61:18080/gsllys/tqLinelossStatis/getYearMonWeekLinelossAnalysisList",
|
||||
type: "POST",
|
||||
contentType: "application/x-www-form-urlencoded",
|
||||
data: JSON.stringify(params)
|
||||
});
|
||||
}
|
||||
|
||||
async function queryWeeklyRank(orgno) {
|
||||
return $.ajax({
|
||||
url: "http://20.76.57.61:18080/gsllys/tqLinelossStatis/getTqLinelossInfoListRank",
|
||||
type: "POST",
|
||||
contentType: "application/x-www-form-urlencoded",
|
||||
data: JSON.stringify({ orgno, page: 1, rows: 20 })
|
||||
});
|
||||
}
|
||||
|
||||
function runWeeklyReport() {
|
||||
return queryWeeklyLineloss("6201001", "2026-04-01", "2026-04-07");
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
203
tests/fixtures/generated_scene/g3_candidate_batch_2026-04-18.json
vendored
Normal file
203
tests/fixtures/generated_scene/g3_candidate_batch_2026-04-18.json
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
{
|
||||
"batchId": "g3-95598-ticket-family-candidates-2026-04-18",
|
||||
"family": "G3",
|
||||
"source": "tests/fixtures/generated_scene/scene_ledger_snapshot_2026-04-18.json",
|
||||
"ledgerClusterLabel": "95598-ticket-family-candidate",
|
||||
"selectionRule": "ledger grouping result == 95598-ticket-family-candidate",
|
||||
"candidateCount": 11,
|
||||
"representativeBaseline": "tests/fixtures/generated_scene/paginated_enrichment",
|
||||
"firstExpansionBaseline": "tests/fixtures/generated_scene/paginated_enrichment_expansion",
|
||||
"promotedBatchExpansionBaselines": [
|
||||
{
|
||||
"fixtureDir": "tests/fixtures/generated_scene/paginated_enrichment_expansion_workorder",
|
||||
"sceneId": "p1-g3-paginated-expansion-workorder-report",
|
||||
"sceneName": "P1 G3 paginated expansion workorder report",
|
||||
"assertions": {
|
||||
"expectedPaginationField": "pageNo",
|
||||
"requiredJoinKey": "workOrderNo",
|
||||
"requiredAggregateRule": "aggregate:sourceType"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fixtureDir": "tests/fixtures/generated_scene/paginated_enrichment_expansion_orderno",
|
||||
"sceneId": "p1-g3-paginated-expansion-orderno-report",
|
||||
"sceneName": "P1 G3 paginated expansion orderno report",
|
||||
"assertions": {
|
||||
"expectedPaginationField": "page",
|
||||
"requiredJoinKey": "orderNo",
|
||||
"requiredAggregateRule": "aggregate:sourceType"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fixtureDir": "tests/fixtures/generated_scene/paginated_enrichment_expansion_source_distribution",
|
||||
"sceneId": "p1-g3-paginated-expansion-source-distribution-report",
|
||||
"sceneName": "P1 G3 paginated expansion source distribution report",
|
||||
"assertions": {
|
||||
"expectedPaginationField": "pageNum",
|
||||
"requiredJoinKey": "ticketNo",
|
||||
"requiredAggregateRule": "aggregate:sourceType"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fixtureDir": "tests/fixtures/generated_scene/paginated_enrichment_expansion_service_risk",
|
||||
"sceneId": "p1-g3-paginated-expansion-service-risk-report",
|
||||
"sceneName": "P1 G3 paginated expansion service risk report",
|
||||
"assertions": {
|
||||
"expectedPaginationField": "pageNo",
|
||||
"requiredJoinKey": "ticketNo",
|
||||
"requiredAggregateRule": "aggregate:riskLevel"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fixtureDir": "tests/fixtures/generated_scene/paginated_enrichment_expansion_timeout_warning",
|
||||
"sceneId": "p1-g3-paginated-expansion-timeout-warning-report",
|
||||
"sceneName": "P1 G3 paginated expansion timeout warning report",
|
||||
"assertions": {
|
||||
"expectedPaginationField": "pageNum",
|
||||
"requiredJoinKey": "ticketNo",
|
||||
"requiredAggregateRule": "aggregate:riskLevel"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fixtureDir": "tests/fixtures/generated_scene/paginated_enrichment_expansion_device_monitor_weekly",
|
||||
"sceneId": "p1-g3-paginated-expansion-device-monitor-weekly-report",
|
||||
"sceneName": "P1 G3 paginated expansion device monitor weekly report",
|
||||
"assertions": {
|
||||
"expectedPaginationField": "pageNo",
|
||||
"requiredJoinKey": "ticketNo",
|
||||
"requiredAggregateRule": "aggregate:sourceType"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fixtureDir": "tests/fixtures/generated_scene/paginated_enrichment_expansion_customer_satisfaction",
|
||||
"sceneId": "p1-g3-paginated-expansion-customer-satisfaction-report",
|
||||
"sceneName": "P1 G3 paginated expansion customer satisfaction report",
|
||||
"assertions": {
|
||||
"expectedPaginationField": "page",
|
||||
"requiredJoinKey": "ticketNo",
|
||||
"requiredAggregateRule": "aggregate:sourceType"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fixtureDir": "tests/fixtures/generated_scene/paginated_enrichment_expansion_repair_return",
|
||||
"sceneId": "p1-g3-paginated-expansion-repair-return-report",
|
||||
"sceneName": "P1 G3 paginated expansion repair return report",
|
||||
"assertions": {
|
||||
"expectedPaginationField": "pageNum",
|
||||
"requiredJoinKey": "ticketNo",
|
||||
"requiredAggregateRule": "aggregate:riskLevel"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fixtureDir": "tests/fixtures/generated_scene/paginated_enrichment_expansion_repair_daily_control",
|
||||
"sceneId": "p1-g3-paginated-expansion-repair-daily-control-report",
|
||||
"sceneName": "P1 G3 paginated expansion repair daily control report",
|
||||
"assertions": {
|
||||
"expectedPaginationField": "pageNo",
|
||||
"requiredJoinKey": "ticketNo",
|
||||
"requiredAggregateRule": "aggregate:riskLevel"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fixtureDir": "tests/fixtures/generated_scene/paginated_enrichment_expansion_business_stats",
|
||||
"sceneId": "p1-g3-paginated-expansion-business-stats-report",
|
||||
"sceneName": "P1 G3 paginated expansion business stats report",
|
||||
"assertions": {
|
||||
"expectedPaginationField": "page",
|
||||
"requiredJoinKey": "ticketNo",
|
||||
"requiredAggregateRule": "aggregate:sourceType"
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectedSharedContract": {
|
||||
"archetype": "paginated_enrichment",
|
||||
"requiredPaginationFields": [
|
||||
"page",
|
||||
"pageNum",
|
||||
"pageSize",
|
||||
"pageNo"
|
||||
],
|
||||
"requiredJoinKeyPatterns": [
|
||||
"ticketNo",
|
||||
"workOrderNo",
|
||||
"orderNo"
|
||||
],
|
||||
"requiredAggregateRulePatterns": [
|
||||
"aggregate:riskLevel",
|
||||
"aggregate:sourceType"
|
||||
]
|
||||
},
|
||||
"candidates": [
|
||||
{
|
||||
"sceneKey": "95598_ticket_12398_process_timeout_detail",
|
||||
"ledgerGroupingResult": "95598-ticket-family-candidate",
|
||||
"ledgerFamilyJudgement": "pending-regroup",
|
||||
"batchRole": "first-expansion-anchor"
|
||||
},
|
||||
{
|
||||
"sceneKey": "95598_ticket_12398_device_monitor_weekly",
|
||||
"ledgerGroupingResult": "95598-ticket-family-candidate",
|
||||
"ledgerFamilyJudgement": "pending-regroup",
|
||||
"batchRole": "seventh-expansion-anchor"
|
||||
},
|
||||
{
|
||||
"sceneKey": "95598_ticket_customer_satisfaction_daily",
|
||||
"ledgerGroupingResult": "95598-ticket-family-candidate",
|
||||
"ledgerFamilyJudgement": "pending-regroup",
|
||||
"batchRole": "eighth-expansion-anchor"
|
||||
},
|
||||
{
|
||||
"sceneKey": "95598_ticket_detail",
|
||||
"ledgerGroupingResult": "95598-ticket-family-candidate",
|
||||
"ledgerFamilyJudgement": "pending-regroup",
|
||||
"batchRole": "p0-anchor"
|
||||
},
|
||||
{
|
||||
"sceneKey": "95598_ticket_repair_return_analysis",
|
||||
"ledgerGroupingResult": "95598-ticket-family-candidate",
|
||||
"ledgerFamilyJudgement": "pending-regroup",
|
||||
"batchRole": "ninth-expansion-anchor"
|
||||
},
|
||||
{
|
||||
"sceneKey": "95598_ticket_repair_daily_control",
|
||||
"ledgerGroupingResult": "95598-ticket-family-candidate",
|
||||
"ledgerFamilyJudgement": "pending-regroup",
|
||||
"batchRole": "tenth-expansion-anchor"
|
||||
},
|
||||
{
|
||||
"sceneKey": "power_supply_service_ticket_business_stats",
|
||||
"ledgerGroupingResult": "95598-ticket-family-candidate",
|
||||
"ledgerFamilyJudgement": "pending-regroup",
|
||||
"batchRole": "eleventh-expansion-anchor"
|
||||
},
|
||||
{
|
||||
"sceneKey": "process_timeout_risk_ticket_detail",
|
||||
"ledgerGroupingResult": "95598-ticket-family-candidate",
|
||||
"ledgerFamilyJudgement": "pending-regroup",
|
||||
"batchRole": "fifth-expansion-anchor"
|
||||
},
|
||||
{
|
||||
"sceneKey": "ticket_timeout_warning_detail",
|
||||
"ledgerGroupingResult": "95598-ticket-family-candidate",
|
||||
"ledgerFamilyJudgement": "pending-regroup",
|
||||
"batchRole": "sixth-expansion-anchor"
|
||||
},
|
||||
{
|
||||
"sceneKey": "ticket_source_distribution_analysis",
|
||||
"ledgerGroupingResult": "95598-ticket-family-candidate",
|
||||
"ledgerFamilyJudgement": "pending-regroup",
|
||||
"batchRole": "fourth-expansion-anchor"
|
||||
},
|
||||
{
|
||||
"sceneKey": "service_risk_ticket_detail",
|
||||
"ledgerGroupingResult": "95598-ticket-family-candidate",
|
||||
"ledgerFamilyJudgement": "pending-regroup",
|
||||
"batchRole": "third-expansion-anchor"
|
||||
}
|
||||
],
|
||||
"notes": [
|
||||
"This batch does not claim that all 11 candidates are already runnable or contract-complete.",
|
||||
"It records that the full current roadmap-selected G3 representative and promoted expansion baselines are the correct family anchor for this ledger cluster.",
|
||||
"The next execution round should continue from these promoted baselines instead of re-selecting the same candidates from the ledger snapshot."
|
||||
]
|
||||
}
|
||||
75
tests/fixtures/generated_scene/g3_enrichment_request_closure_followup_2026-04-19.json
vendored
Normal file
75
tests/fixtures/generated_scene/g3_enrichment_request_closure_followup_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"date": "2026-04-19",
|
||||
"parentRoute": "Route 2: G3 / paginated_enrichment",
|
||||
"parentPlan": "docs/superpowers/plans/2026-04-19-g3-enrichment-request-closure-plan.md",
|
||||
"fixedInputBucket": "paginated_enrichment + g3_enrichment_contract + secondary_request",
|
||||
"beforeCount": 3,
|
||||
"afterResolvedCount": 2,
|
||||
"afterResidualCount": 1,
|
||||
"scenes": [
|
||||
{
|
||||
"sceneId": "sweep-001-95598-12398",
|
||||
"before": {
|
||||
"generationStatus": "fail-closed",
|
||||
"readinessLevel": "C",
|
||||
"enrichmentCount": 0,
|
||||
"primaryBlockers": [
|
||||
"g3_enrichment_contract",
|
||||
"secondary_request"
|
||||
]
|
||||
},
|
||||
"after": {
|
||||
"generationStatus": "pass",
|
||||
"readinessLevel": "A",
|
||||
"enrichmentCount": 2,
|
||||
"residualBlockers": []
|
||||
},
|
||||
"delta": "resolved-to-pass"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-008-95598",
|
||||
"before": {
|
||||
"generationStatus": "fail-closed",
|
||||
"readinessLevel": "C",
|
||||
"enrichmentCount": 0,
|
||||
"primaryBlockers": [
|
||||
"g3_enrichment_contract",
|
||||
"secondary_request",
|
||||
"g3_runtime_scope"
|
||||
]
|
||||
},
|
||||
"after": {
|
||||
"generationStatus": "pass",
|
||||
"readinessLevel": "A",
|
||||
"enrichmentCount": 3,
|
||||
"residualBlockers": []
|
||||
},
|
||||
"delta": "resolved-to-pass"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-002-95598-12398",
|
||||
"before": {
|
||||
"generationStatus": "fail-closed",
|
||||
"readinessLevel": "C",
|
||||
"enrichmentCount": 0,
|
||||
"primaryBlockers": [
|
||||
"g3_enrichment_contract",
|
||||
"secondary_request",
|
||||
"g3_export_plan",
|
||||
"export_plan"
|
||||
]
|
||||
},
|
||||
"after": {
|
||||
"generationStatus": "fail-closed",
|
||||
"readinessLevel": "C",
|
||||
"enrichmentCount": 0,
|
||||
"residualBlockers": [
|
||||
"g3_export_plan",
|
||||
"export_plan"
|
||||
]
|
||||
},
|
||||
"delta": "handed-off-to-export-plan-closure"
|
||||
}
|
||||
],
|
||||
"expectedNextChildPlan": "docs/superpowers/plans/2026-04-19-g3-export-plan-closure-plan.md"
|
||||
}
|
||||
32
tests/fixtures/generated_scene/g3_export_plan_closure_followup_2026-04-19.json
vendored
Normal file
32
tests/fixtures/generated_scene/g3_export_plan_closure_followup_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"date": "2026-04-19",
|
||||
"parentRoute": "Route 2: G3 / paginated_enrichment",
|
||||
"parentPlan": "docs/superpowers/plans/2026-04-19-g3-export-plan-closure-plan.md",
|
||||
"fixedInputBucket": "paginated_enrichment + g3_export_plan + export_plan",
|
||||
"beforeCount": 1,
|
||||
"afterResolvedCount": 1,
|
||||
"afterResidualCount": 0,
|
||||
"scenes": [
|
||||
{
|
||||
"sceneId": "sweep-002-95598-12398",
|
||||
"before": {
|
||||
"generationStatus": "fail-closed",
|
||||
"readinessLevel": "C",
|
||||
"exportEntry": null,
|
||||
"primaryBlockers": [
|
||||
"g3_export_plan",
|
||||
"export_plan"
|
||||
]
|
||||
},
|
||||
"after": {
|
||||
"generationStatus": "pass",
|
||||
"readinessLevel": "A",
|
||||
"exportEntry": "exportWord",
|
||||
"residualBlockers": []
|
||||
},
|
||||
"delta": "resolved-to-pass"
|
||||
}
|
||||
],
|
||||
"residualRoute2CountAfterThisPlan": 0,
|
||||
"expectedNextChildPlan": "docs/superpowers/plans/2026-04-19-g3-residual-contract-closure-plan.md"
|
||||
}
|
||||
66
tests/fixtures/generated_scene/g3_g8_mixed_boundary/index.html
vendored
Normal file
66
tests/fixtures/generated_scene/g3_g8_mixed_boundary/index.html
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>G3 G8 Mixed Boundary</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const pagesDetail = {
|
||||
loginPath: "http://south.95598.sgcc.com.cn/bs/bsp/jsp/login.jsp",
|
||||
mainPath: "http://south.95598.sgcc.com.cn/bs/bsp/jsp/main1.jsp"
|
||||
}
|
||||
|
||||
async function getDataCallBack1(targeturl, actionurl, responseText) {
|
||||
let data = { TabTi: { rows: [{ cols: [] }, { cols: [] }] }, ColumnNames: ["appNo", "custNo"] }
|
||||
data.TabTi.rows.shift()
|
||||
let this_rows = data.TabTi.rows.map((e) => e["cols"])
|
||||
return this_rows
|
||||
}
|
||||
|
||||
function getDataCallBack2(targeturl, actionurl, responseText) {
|
||||
return responseText
|
||||
}
|
||||
|
||||
async function getTicketDetail(appNo) {
|
||||
return axios.post("http://south.95598.sgcc.com.cn/bs/businessaccept/queryTicketDetail/query.so", {
|
||||
appNo: appNo
|
||||
})
|
||||
}
|
||||
|
||||
function exportExcel(config) {
|
||||
return docExport(config)
|
||||
}
|
||||
|
||||
function runScene(mac, startHandleTime, endHandleTime) {
|
||||
let path = moment(endHandleTime).diff(moment(), 'days') < -90
|
||||
? '/bs/businessaccept/queryHisS95598WkstGrid/query.so'
|
||||
: '/bs/businessaccept/newQueryS95598WkstGrid/query.so'
|
||||
|
||||
BrowserAction(
|
||||
'sgBrowerserJsAjax2',
|
||||
pagesDetail.mainPath,
|
||||
`${path}?appNo=&custNo=&org.sotower.web.taglib.util.PAGEPOLITPAGESIZE_grid=15&org.sotower.web.taglib.util.PAGEPOLITCURRENTPAGEINDEX_grid=${mac.page1}&org.sotower.web.taglib.util.RESETPAGEINDEX_grid=false`,
|
||||
"getDataCallBack1"
|
||||
)
|
||||
|
||||
axios.post("http://localhost:13313/configServices/selectData", JSON.stringify({
|
||||
tableName: "work_order_detail_for95598",
|
||||
query: "WHERE appNo >= '20260101'"
|
||||
}), {
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
})
|
||||
|
||||
let exportConfig = {
|
||||
url: "http://localhost:13313/SurfaceServices/personalBread/export/faultYoYExportXLSX"
|
||||
}
|
||||
|
||||
if (mac.page1 === mac.total1) {
|
||||
exportExcel(exportConfig)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
82
tests/fixtures/generated_scene/g3_residual_4_workflow_evidence_closure_followup_2026-04-19.json
vendored
Normal file
82
tests/fixtures/generated_scene/g3_residual_4_workflow_evidence_closure_followup_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"runDate": "2026-04-19",
|
||||
"parentPlan": "docs/superpowers/plans/2026-04-19-g3-residual-4-workflow-evidence-closure-plan.md",
|
||||
"outputRoot": "D:\\data\\ideaSpace\\rust\\sgClaw\\claw-new\\examples\\g3_residual_4_workflow_evidence_closure_2026-04-19",
|
||||
"summary": {
|
||||
"total": 4,
|
||||
"pass": 4,
|
||||
"failClosed": 0,
|
||||
"sourceUnreadable": 0,
|
||||
"unknown": 0
|
||||
},
|
||||
"scenes": [
|
||||
{
|
||||
"sceneId": "sweep-007-scene",
|
||||
"sceneName": "95598供电服务月报",
|
||||
"exitCode": 0,
|
||||
"durationSeconds": 10.13,
|
||||
"workflowArchetype": "paginated_enrichment",
|
||||
"readinessLevel": "A",
|
||||
"generationStatus": "",
|
||||
"routeStatus": "pass",
|
||||
"missingPieces": [
|
||||
|
||||
],
|
||||
"risks": [
|
||||
|
||||
],
|
||||
"reportPath": "D:\\data\\ideaSpace\\rust\\sgClaw\\claw-new\\examples\\g3_residual_4_workflow_evidence_closure_2026-04-19\\skills\\sweep-007-scene\\references\\generation-report.json"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-039-scene",
|
||||
"sceneName": "故障报修工单信息统计表",
|
||||
"exitCode": 0,
|
||||
"durationSeconds": 5.11,
|
||||
"workflowArchetype": "paginated_enrichment",
|
||||
"readinessLevel": "A",
|
||||
"generationStatus": "",
|
||||
"routeStatus": "pass",
|
||||
"missingPieces": [
|
||||
|
||||
],
|
||||
"risks": [
|
||||
|
||||
],
|
||||
"reportPath": "D:\\data\\ideaSpace\\rust\\sgClaw\\claw-new\\examples\\g3_residual_4_workflow_evidence_closure_2026-04-19\\skills\\sweep-039-scene\\references\\generation-report.json"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-068-scene",
|
||||
"sceneName": "输变电设备运行分析报告",
|
||||
"exitCode": 0,
|
||||
"durationSeconds": 10.15,
|
||||
"workflowArchetype": "paginated_enrichment",
|
||||
"readinessLevel": "A",
|
||||
"generationStatus": "",
|
||||
"routeStatus": "pass",
|
||||
"missingPieces": [
|
||||
|
||||
],
|
||||
"risks": [
|
||||
|
||||
],
|
||||
"reportPath": "D:\\data\\ideaSpace\\rust\\sgClaw\\claw-new\\examples\\g3_residual_4_workflow_evidence_closure_2026-04-19\\skills\\sweep-068-scene\\references\\generation-report.json"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-084-scene",
|
||||
"sceneName": "巡视计划完成情况自动检索",
|
||||
"exitCode": 0,
|
||||
"durationSeconds": 5.09,
|
||||
"workflowArchetype": "paginated_enrichment",
|
||||
"readinessLevel": "A",
|
||||
"generationStatus": "",
|
||||
"routeStatus": "pass",
|
||||
"missingPieces": [
|
||||
|
||||
],
|
||||
"risks": [
|
||||
|
||||
],
|
||||
"reportPath": "D:\\data\\ideaSpace\\rust\\sgClaw\\claw-new\\examples\\g3_residual_4_workflow_evidence_closure_2026-04-19\\skills\\sweep-084-scene\\references\\generation-report.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
18
tests/fixtures/generated_scene/g3_residual_contract_closure_2026-04-19.json
vendored
Normal file
18
tests/fixtures/generated_scene/g3_residual_contract_closure_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"date": "2026-04-19",
|
||||
"parentRoute": "Route 2: G3 / paginated_enrichment",
|
||||
"parentPlan": "docs/superpowers/plans/2026-04-19-g3-residual-contract-closure-plan.md",
|
||||
"inputSources": [
|
||||
"tests/fixtures/generated_scene/g3_enrichment_request_closure_followup_2026-04-19.json",
|
||||
"tests/fixtures/generated_scene/g3_export_plan_closure_followup_2026-04-19.json"
|
||||
],
|
||||
"residualBeforeThisPlan": 0,
|
||||
"implementedCorrectionSlice": false,
|
||||
"residualAfterThisPlan": 0,
|
||||
"residualGroups": [],
|
||||
"route2Status": "completed",
|
||||
"handoff": {
|
||||
"nextRoute": "Route 3: G2 / multi_mode_request",
|
||||
"nextPlan": "docs/superpowers/plans/2026-04-19-g2-remaining-fail-closed-closure-plan.md"
|
||||
}
|
||||
}
|
||||
56
tests/fixtures/generated_scene/g6_host_bridge_callback_semantics_2026-04-19.json
vendored
Normal file
56
tests/fixtures/generated_scene/g6_host_bridge_callback_semantics_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"decisionDate": "2026-04-19",
|
||||
"scope": "g6-host-bridge-callback-semantics",
|
||||
"startingState": {
|
||||
"targetGroup": "G6",
|
||||
"realExecutionOutOfScope": true,
|
||||
"implementationOutOfScope": true,
|
||||
"heldGroups": [
|
||||
"G8"
|
||||
]
|
||||
},
|
||||
"completionStates": [
|
||||
{
|
||||
"state": "ok",
|
||||
"definition": "All callback_request endpoints return successful callback results and no blockedReason or fatalError is raised."
|
||||
},
|
||||
{
|
||||
"state": "partial",
|
||||
"definition": "At least one callback_request endpoint returns a non-ok callback result while the overall flow is not blocked and no fatalError is raised."
|
||||
},
|
||||
{
|
||||
"state": "blocked",
|
||||
"definition": "The flow is stopped by page-context validation or host-bridge unavailability before callback completion can be accepted."
|
||||
},
|
||||
{
|
||||
"state": "error",
|
||||
"definition": "A fatal execution error is raised outside the bounded blocked-state path."
|
||||
}
|
||||
],
|
||||
"semanticRules": [
|
||||
{
|
||||
"rule": "blocked_has_priority",
|
||||
"summary": "blocked overrides all callback result states"
|
||||
},
|
||||
{
|
||||
"rule": "fatal_error_maps_to_error",
|
||||
"summary": "fatalError maps directly to error when blocked is absent"
|
||||
},
|
||||
{
|
||||
"rule": "non_ok_callback_maps_to_partial",
|
||||
"summary": "any non-ok callback result maps to partial when blocked and fatalError are absent"
|
||||
},
|
||||
{
|
||||
"rule": "all_ok_maps_to_ok",
|
||||
"summary": "all callback results ok maps to ok when blocked and fatalError are absent"
|
||||
}
|
||||
],
|
||||
"selectedFollowup": {
|
||||
"design": "docs/superpowers/specs/2026-04-19-g6-host-bridge-callback-state-verification-design.md",
|
||||
"plan": "docs/superpowers/plans/2026-04-19-g6-host-bridge-callback-state-verification-plan.md"
|
||||
},
|
||||
"notes": [
|
||||
"This slice publishes only the bounded completion-state semantics.",
|
||||
"No G6 real-sample execution or host-runtime implementation is opened here."
|
||||
]
|
||||
}
|
||||
44
tests/fixtures/generated_scene/g6_host_bridge_callback_state_verification_2026-04-19.json
vendored
Normal file
44
tests/fixtures/generated_scene/g6_host_bridge_callback_state_verification_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"decisionDate": "2026-04-19",
|
||||
"scope": "g6-host-bridge-callback-state-verification",
|
||||
"startingState": {
|
||||
"targetGroup": "G6",
|
||||
"realExecutionOutOfScope": true,
|
||||
"implementationOutOfScope": true,
|
||||
"heldGroups": [
|
||||
"G8"
|
||||
]
|
||||
},
|
||||
"verificationTargets": [
|
||||
{
|
||||
"state": "ok",
|
||||
"evidenceRequirement": "all callback_request results are ok and neither blockedReason nor fatalError is present"
|
||||
},
|
||||
{
|
||||
"state": "partial",
|
||||
"evidenceRequirement": "at least one callback_request result is non-ok while blockedReason and fatalError are both absent"
|
||||
},
|
||||
{
|
||||
"state": "blocked",
|
||||
"evidenceRequirement": "pageValidation fails or host bridge returns a blocked result before callback success can be accepted"
|
||||
},
|
||||
{
|
||||
"state": "error",
|
||||
"evidenceRequirement": "fatalError is present while blockedReason is absent"
|
||||
}
|
||||
],
|
||||
"verificationPriority": [
|
||||
"blocked",
|
||||
"error",
|
||||
"partial",
|
||||
"ok"
|
||||
],
|
||||
"selectedFollowup": {
|
||||
"design": "docs/superpowers/specs/2026-04-19-g6-host-bridge-entry-readiness-design.md",
|
||||
"plan": "docs/superpowers/plans/2026-04-19-g6-host-bridge-entry-readiness-plan.md"
|
||||
},
|
||||
"notes": [
|
||||
"This slice publishes only bounded verification targets for callback states.",
|
||||
"No G6 real-sample execution or host-runtime implementation is opened here."
|
||||
]
|
||||
}
|
||||
52
tests/fixtures/generated_scene/g6_host_bridge_entry_gate_2026-04-19.json
vendored
Normal file
52
tests/fixtures/generated_scene/g6_host_bridge_entry_gate_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"decisionDate": "2026-04-19",
|
||||
"scope": "g6-host-bridge-entry-gate",
|
||||
"startingState": {
|
||||
"targetGroup": "G6",
|
||||
"realExecutionOutOfScope": true,
|
||||
"implementationOutOfScope": true,
|
||||
"heldGroups": [
|
||||
"G8"
|
||||
]
|
||||
},
|
||||
"hardGateConditions": [
|
||||
{
|
||||
"name": "host-bridge-action-invocation-defined",
|
||||
"status": "required",
|
||||
"failureReason": "g6_bridge_invocation_semantics_missing"
|
||||
},
|
||||
{
|
||||
"name": "callback-request-completion-defined",
|
||||
"status": "required",
|
||||
"failureReason": "g6_callback_completion_semantics_missing"
|
||||
},
|
||||
{
|
||||
"name": "callback-state-verification-targets-defined",
|
||||
"status": "required",
|
||||
"failureReason": "g6_callback_state_targets_missing"
|
||||
}
|
||||
],
|
||||
"softConditions": [
|
||||
{
|
||||
"name": "host-runtime-transport-implementation",
|
||||
"status": "optional-later"
|
||||
},
|
||||
{
|
||||
"name": "real-sample-execution-proof",
|
||||
"status": "optional-later"
|
||||
}
|
||||
],
|
||||
"failCloseReasons": [
|
||||
"g6_bridge_invocation_semantics_missing",
|
||||
"g6_callback_completion_semantics_missing",
|
||||
"g6_callback_state_targets_missing"
|
||||
],
|
||||
"selectedFollowup": {
|
||||
"design": "docs/superpowers/specs/2026-04-19-g6-host-bridge-entry-gate-verification-design.md",
|
||||
"plan": "docs/superpowers/plans/2026-04-19-g6-host-bridge-entry-gate-verification-plan.md"
|
||||
},
|
||||
"notes": [
|
||||
"This slice publishes only bounded future entry-gate conditions.",
|
||||
"No G6 real-sample execution or host-runtime implementation is opened here."
|
||||
]
|
||||
}
|
||||
53
tests/fixtures/generated_scene/g6_host_bridge_entry_readiness_2026-04-19.json
vendored
Normal file
53
tests/fixtures/generated_scene/g6_host_bridge_entry_readiness_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"decisionDate": "2026-04-19",
|
||||
"scope": "g6-host-bridge-entry-readiness",
|
||||
"startingState": {
|
||||
"targetGroup": "G6",
|
||||
"realExecutionOutOfScope": true,
|
||||
"implementationOutOfScope": true,
|
||||
"heldGroups": [
|
||||
"G8"
|
||||
]
|
||||
},
|
||||
"requiredCriteria": [
|
||||
{
|
||||
"name": "host-bridge-action-invocation-defined",
|
||||
"status": "required",
|
||||
"reason": "A future G6 entry cannot open without an explicit bridge-action invocation semantic."
|
||||
},
|
||||
{
|
||||
"name": "callback-request-completion-defined",
|
||||
"status": "required",
|
||||
"reason": "A future G6 entry cannot open without an explicit callback completion semantic."
|
||||
},
|
||||
{
|
||||
"name": "callback-state-verification-targets-defined",
|
||||
"status": "required",
|
||||
"reason": "A future G6 entry cannot open without bounded verification targets for ok/partial/blocked/error."
|
||||
}
|
||||
],
|
||||
"optionalCriteria": [
|
||||
{
|
||||
"name": "host-runtime-transport-implementation",
|
||||
"status": "optional-later",
|
||||
"reason": "Must remain outside this readiness slice."
|
||||
},
|
||||
{
|
||||
"name": "real-sample-execution-proof",
|
||||
"status": "optional-later",
|
||||
"reason": "Belongs to a later execution slice, not to readiness modeling."
|
||||
}
|
||||
],
|
||||
"minimalReadinessThreshold": {
|
||||
"level": "semantic-ready",
|
||||
"definition": "G6 may be considered ready for a later bounded entry slice only after all required semantic criteria are explicit while direct runtime implementation remains out of scope."
|
||||
},
|
||||
"selectedFollowup": {
|
||||
"design": "docs/superpowers/specs/2026-04-19-g6-host-bridge-entry-gate-design.md",
|
||||
"plan": "docs/superpowers/plans/2026-04-19-g6-host-bridge-entry-gate-plan.md"
|
||||
},
|
||||
"notes": [
|
||||
"This slice publishes only bounded entry-readiness criteria.",
|
||||
"No G6 real-sample execution or host-runtime implementation is opened here."
|
||||
]
|
||||
}
|
||||
47
tests/fixtures/generated_scene/g6_host_bridge_execution_semantics_2026-04-19.json
vendored
Normal file
47
tests/fixtures/generated_scene/g6_host_bridge_execution_semantics_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"decisionDate": "2026-04-19",
|
||||
"scope": "g6-host-bridge-execution-semantics",
|
||||
"startingState": {
|
||||
"targetGroup": "G6",
|
||||
"realExecutionOutOfScope": true,
|
||||
"implementationOutOfScope": true,
|
||||
"heldGroups": [
|
||||
"G8"
|
||||
]
|
||||
},
|
||||
"semanticModel": {
|
||||
"bridgeInvocation": {
|
||||
"name": "host-bridge-action-invocation",
|
||||
"summary": "A bounded semantic must define how a host bridge action is identified, invoked, and interpreted before any real G6 sample may be executed."
|
||||
},
|
||||
"callbackCompletion": {
|
||||
"name": "callback-request-completion",
|
||||
"summary": "A bounded semantic must define when callback_request steps count as complete, partial, blocked, or error during later real execution."
|
||||
}
|
||||
},
|
||||
"semanticBoundaries": [
|
||||
{
|
||||
"slice": "bridge_action_invocation",
|
||||
"status": "selected",
|
||||
"reason": "Directly matches the existing invokeHostBridge semantic seam in the generated G6 runtime shape."
|
||||
},
|
||||
{
|
||||
"slice": "callback_completion_semantics",
|
||||
"status": "selected",
|
||||
"reason": "Directly matches the existing callbackEndpoints and callback result accumulation seam in the generated G6 runtime shape."
|
||||
},
|
||||
{
|
||||
"slice": "host_runtime_transport_rebuild",
|
||||
"status": "out-of-scope",
|
||||
"reason": "Must remain outside this bounded semantic slice."
|
||||
}
|
||||
],
|
||||
"selectedFollowup": {
|
||||
"design": "docs/superpowers/specs/2026-04-19-g6-host-bridge-callback-semantics-design.md",
|
||||
"plan": "docs/superpowers/plans/2026-04-19-g6-host-bridge-callback-semantics-plan.md"
|
||||
},
|
||||
"notes": [
|
||||
"This slice publishes only the minimum semantic boundary.",
|
||||
"No G6 real-sample execution or host-runtime implementation is opened here."
|
||||
]
|
||||
}
|
||||
42
tests/fixtures/generated_scene/g6_host_bridge_prerequisites_2026-04-19.json
vendored
Normal file
42
tests/fixtures/generated_scene/g6_host_bridge_prerequisites_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"decisionDate": "2026-04-19",
|
||||
"scope": "g6-host-bridge-prerequisites",
|
||||
"startingState": {
|
||||
"targetGroup": "G6",
|
||||
"executionOutOfScope": true,
|
||||
"reopenedGroups": [],
|
||||
"heldGroups": [
|
||||
"G8"
|
||||
]
|
||||
},
|
||||
"blockedCapability": {
|
||||
"name": "host-bridge-real-execution-semantics",
|
||||
"summary": "The generator already models host_bridge_workflow, but the next real-sample step is blocked by the lack of a bounded semantic contract for how host bridge calls are issued, awaited, and validated during real execution.",
|
||||
"boundedInsteadOfBroadRuntime": true
|
||||
},
|
||||
"capabilityBreakdown": [
|
||||
{
|
||||
"slice": "bridge_action_invocation",
|
||||
"status": "needed",
|
||||
"reason": "A real sample needs a bounded semantic description of how sgBrowserExcuteJsCode-style actions are invoked."
|
||||
},
|
||||
{
|
||||
"slice": "callback_completion_semantics",
|
||||
"status": "needed",
|
||||
"reason": "A real sample needs a bounded semantic description of how callback_request steps are recognized as complete or failed."
|
||||
},
|
||||
{
|
||||
"slice": "host_runtime_platform_rebuild",
|
||||
"status": "out-of-scope",
|
||||
"reason": "The prerequisite slice must stay narrower than broad host-runtime implementation."
|
||||
}
|
||||
],
|
||||
"selectedFollowup": {
|
||||
"design": "docs/superpowers/specs/2026-04-19-g6-host-bridge-execution-semantics-design.md",
|
||||
"plan": "docs/superpowers/plans/2026-04-19-g6-host-bridge-execution-semantics-plan.md"
|
||||
},
|
||||
"notes": [
|
||||
"This slice isolates the minimum blocked capability instead of broadening into host-runtime implementation.",
|
||||
"No G6 real-sample execution is opened under this plan."
|
||||
]
|
||||
}
|
||||
59
tests/fixtures/generated_scene/g6_host_bridge_workflow/index.html
vendored
Normal file
59
tests/fixtures/generated_scene/g6_host_bridge_workflow/index.html
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="sgclaw-scene-kind" content="report_collection" />
|
||||
<meta name="sgclaw-target-url" content="http://yx.gs.sgcc.com.cn/" />
|
||||
<meta name="sgclaw-expected-domain" content="yx.gscc.com.cn" />
|
||||
<title>Host Bridge Workflow Fixture</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const sourceUrl = "http://yx.gs.sgcc.com.cn/meter";
|
||||
|
||||
async function runInspectionRateReport() {
|
||||
const todo = await BrowserAction("sgBrowerserJsAjax2", {
|
||||
url: "http://localhost:13313/browser/action/callback",
|
||||
callback: "loadTodo"
|
||||
});
|
||||
const todoRows = await getWorkOrderToDoList(todo.batchNo);
|
||||
for (const item of todoRows.rows || []) {
|
||||
await sgBrowserExcuteJsCode("openPlanForm", item.wkOrderNo);
|
||||
const plan = await queryMeterPlanFormulateApp(item.wkOrderNo);
|
||||
const details = await queryMeterPlanDtlForAddMeter(plan.planNo);
|
||||
if (details.rows && details.rows.length > 0) {
|
||||
item.finishRate = details.rows.filter(row => row.finished).length / details.rows.length;
|
||||
}
|
||||
}
|
||||
return todoRows;
|
||||
}
|
||||
|
||||
async function getWorkOrderToDoList(batchNo) {
|
||||
return $.ajax({
|
||||
url: "http://yx.gscc.com.cn/meter/getWorkOrderToDoList",
|
||||
type: "POST",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({ batchNo })
|
||||
});
|
||||
}
|
||||
|
||||
async function queryMeterPlanFormulateApp(wkOrderNo) {
|
||||
return $.ajax({
|
||||
url: "http://yx.gscc.com.cn/meter/queryMeterPlanFormulateApp",
|
||||
type: "POST",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({ wkOrderNo })
|
||||
});
|
||||
}
|
||||
|
||||
async function queryMeterPlanDtlForAddMeter(planNo) {
|
||||
return $.ajax({
|
||||
url: "http://yx.gscc.com.cn/meter/queryMeterPlanDtlForAddMeter",
|
||||
type: "POST",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({ planNo })
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
73
tests/fixtures/generated_scene/g7_multi_endpoint_inventory/index.html
vendored
Normal file
73
tests/fixtures/generated_scene/g7_multi_endpoint_inventory/index.html
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="sgclaw-scene-kind" content="report_collection" />
|
||||
<meta name="sgclaw-target-url" content="http://yx.gscc.com.cn/" />
|
||||
<meta name="sgclaw-expected-domain" content="yx.gscc.com.cn" />
|
||||
<title>Multi Endpoint Inventory Fixture</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const sourceUrl = "http://yx.gscc.com.cn/asset";
|
||||
|
||||
async function runAssetInventory() {
|
||||
const meter = await assetStatsQueryMeter("04");
|
||||
const it = await assetStatsQueryIt("04");
|
||||
const terminal = await assetStatsQueryAcqTrml("04");
|
||||
const common = await assetStatsQueryMeterCommonModule("04");
|
||||
const module = await assetStatsQueryJlGnModule("04");
|
||||
return aggregateInventory([meter, it, terminal, common, module]);
|
||||
}
|
||||
|
||||
async function assetStatsQueryMeter(month) {
|
||||
return $.ajax({
|
||||
url: "http://yx.gscc.com.cn/asset/assetStatsQueryMeter",
|
||||
type: "POST",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({ month, assetType: "meter" })
|
||||
});
|
||||
}
|
||||
|
||||
async function assetStatsQueryIt(month) {
|
||||
return $.ajax({
|
||||
url: "http://yx.gscc.com.cn/asset/assetStatsQueryIt",
|
||||
type: "POST",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({ month, assetType: "it" })
|
||||
});
|
||||
}
|
||||
|
||||
async function assetStatsQueryAcqTrml(month) {
|
||||
return $.ajax({
|
||||
url: "http://yx.gscc.com.cn/asset/assetStatsQueryAcqTrml",
|
||||
type: "POST",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({ month, assetType: "terminal" })
|
||||
});
|
||||
}
|
||||
|
||||
async function assetStatsQueryMeterCommonModule(month) {
|
||||
return $.ajax({
|
||||
url: "http://yx.gscc.com.cn/asset/assetStatsQueryMeterCommonModule",
|
||||
type: "POST",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({ month, assetType: "common_module" })
|
||||
});
|
||||
}
|
||||
|
||||
async function assetStatsQueryJlGnModule(month) {
|
||||
return $.ajax({
|
||||
url: "http://yx.gscc.com.cn/asset/assetStatsQueryJlGnModule",
|
||||
type: "POST",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({ month, assetType: "function_module" })
|
||||
});
|
||||
}
|
||||
|
||||
function aggregateInventory(parts) {
|
||||
return parts.flatMap(part => part.rows || []);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
48
tests/fixtures/generated_scene/g8_local_doc_pipeline/index.html
vendored
Normal file
48
tests/fixtures/generated_scene/g8_local_doc_pipeline/index.html
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="sgclaw-scene-kind" content="report_collection" />
|
||||
<meta name="sgclaw-target-url" content="http://south.95598.sgcc.com.cn/" />
|
||||
<meta name="sgclaw-expected-domain" content="south.95598.sgcc.com.cn" />
|
||||
<title>Local Document Pipeline Fixture</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const sourceUrl = "http://south.95598.sgcc.com.cn/report";
|
||||
|
||||
async function runServiceMonthlyReport() {
|
||||
const sourceRows = await BrowserAction("sgBrowerserJsAjax2", {
|
||||
url: "http://localhost:13313/configServices/selectData",
|
||||
callback: "selectData"
|
||||
});
|
||||
await $.ajax({
|
||||
url: "http://localhost:13313/configServices/selectData",
|
||||
type: "POST",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({ tableName: "service_monthly_raw", rows: sourceRows })
|
||||
});
|
||||
const summary = await definedSqlQuery(
|
||||
"select city, count(*) as total from service_monthly_raw group by city"
|
||||
);
|
||||
return docExport({
|
||||
template: "95598-service-monthly.docx",
|
||||
data: summary
|
||||
});
|
||||
}
|
||||
|
||||
async function definedSqlQuery(sql) {
|
||||
return $.ajax({
|
||||
url: "http://localhost:13313/configServices/definedSqlQuery",
|
||||
type: "POST",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({ sql })
|
||||
});
|
||||
}
|
||||
|
||||
function docExport(payload) {
|
||||
return BrowserAction("docExport", payload);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"route": "embedded_dictionary_extraction_hardening",
|
||||
"date": "2026-04-21",
|
||||
"status": "completed",
|
||||
"bucket": {
|
||||
"focus_archetype": "multi_mode_request",
|
||||
"anchor_scene": "sweep-030-scene",
|
||||
"source_driven_dictionary_slice": true
|
||||
},
|
||||
"changes": {
|
||||
"source_side_extraction_added": true,
|
||||
"generated_org_dictionary_from_source": true,
|
||||
"runtime_resolver_changed": false,
|
||||
"materialized_skills_changed": false
|
||||
},
|
||||
"verified": {
|
||||
"real_source_dictionary_codes": [
|
||||
"62401",
|
||||
"62408"
|
||||
],
|
||||
"fixture_dictionary_remains_empty_without_source_evidence": true,
|
||||
"tests": [
|
||||
"cargo test --test scene_generator_test analyzer_extracts_embedded_org_dictionary_from_sweep_030_source -- --nocapture",
|
||||
"cargo test --test scene_generator_test generator_writes_real_sweep_030_org_dictionary_from_embedded_source -- --nocapture",
|
||||
"cargo test --test scene_generator_test generator_writes_multi_mode_package_from_deterministic_analysis -- --nocapture"
|
||||
]
|
||||
},
|
||||
"residuals": [
|
||||
"dictionary extraction currently anchors on source evidence files such as city.js/dict.js/enum.js",
|
||||
"full tree preservation is not implemented in this slice",
|
||||
"route does not rematerialize 102 skills"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"route": "alias_generation_hardening",
|
||||
"plan": "docs/superpowers/plans/2026-04-20-generated-scene-invocation-alias-generation-hardening-plan.md",
|
||||
"date": "2026-04-21",
|
||||
"status": "completed",
|
||||
"scope": {
|
||||
"slice": "first reusable deterministic include keyword expansion",
|
||||
"bucket": "high-risk multi-mode request wording divergence, anchored by sweep-030-scene",
|
||||
"not_in_scope": [
|
||||
"runtime dispatch scoring",
|
||||
"service console behavior",
|
||||
"manual edits to final materialized scene.toml",
|
||||
"full 84-scene alias closure",
|
||||
"rematerialization refresh",
|
||||
"validation refresh"
|
||||
]
|
||||
},
|
||||
"implementation": {
|
||||
"changed_files": [
|
||||
"src/generated_scene/generator.rs",
|
||||
"tests/scene_generator_test.rs"
|
||||
],
|
||||
"summary": [
|
||||
"Generated deterministic include_keywords now preserve canonical scene names and page title keywords.",
|
||||
"Generated deterministic include_keywords now add normalized punctuation-stripped aliases.",
|
||||
"Month/week combined names can produce month-specific and week-specific invocation aliases.",
|
||||
"Line-loss style names can produce compact aliases such as taiqu-lineloss and lineloss-big-data."
|
||||
]
|
||||
},
|
||||
"anchor_scene": {
|
||||
"scene_id": "sweep-030-scene",
|
||||
"expected_generated_aliases": [
|
||||
"line-loss big data monthly line-loss statistics analysis",
|
||||
"line-loss big data weekly line-loss statistics analysis",
|
||||
"taiqu line-loss"
|
||||
],
|
||||
"reason": "These aliases match the real operator wording found during the intranet sweep-030 debugging process."
|
||||
},
|
||||
"verification": {
|
||||
"passed": [
|
||||
"cargo test --test scene_generator_test generator_writes_real_sweep_030_org_dictionary_from_embedded_source -- --nocapture",
|
||||
"cargo test --test scene_generator_modes_test -- --nocapture"
|
||||
],
|
||||
"warnings": [
|
||||
"Existing dead_code warnings in callback/openxml/generated_scene code remain.",
|
||||
"Existing unreachable_code warning in scene_generator_test.rs remains."
|
||||
]
|
||||
},
|
||||
"next_route": "generated_scene_runtime_semantics_rematerialization_refresh"
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
{
|
||||
"summary": {
|
||||
"runDate": "2026-04-21",
|
||||
"plan": "docs/superpowers/plans/2026-04-21-generated-scene-local-doc-pipeline-residual-closure-plan.md",
|
||||
"scope": "bounded local_doc_pipeline residual closure",
|
||||
"totalResidualScenes": 6,
|
||||
"analyzerEvidenceRecovered": 6,
|
||||
"generatorPackageRecoveryValidated": 6,
|
||||
"rematerializationRerun": false,
|
||||
"validationRefreshRerun": false
|
||||
},
|
||||
"residualScenes": [
|
||||
{
|
||||
"sceneId": "sweep-025-scene",
|
||||
"sceneName": "local-doc residual sweep-025",
|
||||
"recoveredEvidence": [
|
||||
"reportLogQuery",
|
||||
"reportLogSet",
|
||||
"reportFileOpen",
|
||||
"docExport"
|
||||
],
|
||||
"closureBasis": "fault details XLSX export plus report log/local file evidence"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-047-scene",
|
||||
"sceneName": "local-doc residual sweep-047",
|
||||
"recoveredEvidence": [
|
||||
"reportLogQuery",
|
||||
"reportLogSet",
|
||||
"reportLogDelete",
|
||||
"docTemplateTransform",
|
||||
"docExport"
|
||||
],
|
||||
"closureBasis": "docx template transform and exportImageDocs/local report log evidence"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-050-scene",
|
||||
"sceneName": "local-doc residual sweep-050",
|
||||
"recoveredEvidence": [
|
||||
"reportLogQuery",
|
||||
"reportLogSet",
|
||||
"reportLogDelete",
|
||||
"docExport"
|
||||
],
|
||||
"closureBasis": "webpack-bundled uploadWord/setWord/aSaveFile document export evidence plus report log"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-052-scene",
|
||||
"sceneName": "local-doc residual sweep-052",
|
||||
"recoveredEvidence": [
|
||||
"reportLogQuery",
|
||||
"reportLogSet",
|
||||
"reportLogDelete",
|
||||
"docTemplateTransform",
|
||||
"docExport"
|
||||
],
|
||||
"closureBasis": "docx template path, exportWord/exportImageDocs, and report log evidence"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-062-scene",
|
||||
"sceneName": "local-doc residual sweep-062",
|
||||
"recoveredEvidence": [
|
||||
"reportLogQuery",
|
||||
"reportLogSet",
|
||||
"reportLogDelete",
|
||||
"docTemplateTransform",
|
||||
"docExport"
|
||||
],
|
||||
"closureBasis": "docx template path, exportWord/exportImageDocs, and report log evidence"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-087-scene",
|
||||
"sceneName": "local-doc residual sweep-087",
|
||||
"recoveredEvidence": [
|
||||
"reportLogQuery",
|
||||
"reportLogSet",
|
||||
"reportLogDelete",
|
||||
"docExport"
|
||||
],
|
||||
"closureBasis": "api/genword plus aSaveFile/report log document generation evidence"
|
||||
}
|
||||
],
|
||||
"implementedReusableEvidenceTokens": [
|
||||
"exportImageDocs",
|
||||
"exportWordFile",
|
||||
"uploadWord",
|
||||
"setWord",
|
||||
"aSaveFile",
|
||||
"mammoth.convertToHtml",
|
||||
"faultDetailsExportXLSX",
|
||||
"api/genword",
|
||||
"/docxs/",
|
||||
"ReportServices/Api/readeFile"
|
||||
],
|
||||
"validation": {
|
||||
"passed": [
|
||||
"cargo test --test scene_generator_test analyzer_recovers_local_doc_residual_export_workflow_evidence -- --nocapture",
|
||||
"cargo test --test scene_generator_test generator_recovers_local_doc_residual_packages_from_source_evidence -- --nocapture",
|
||||
"cargo test --test scene_generator_test generator_writes_g8_local_doc_pipeline_package -- --nocapture",
|
||||
"cargo test --test scene_generator_test generator_blocks_incomplete_g8_local_doc_pipeline_contract -- --nocapture",
|
||||
"cargo test --test scene_generator_test generator_accepts_g8_local_doc_select_data_contract -- --nocapture"
|
||||
],
|
||||
"knownWarnings": [
|
||||
"existing dead_code warnings in callback_host/openxml/generator",
|
||||
"existing unreachable_code warning in scene_generator_test"
|
||||
]
|
||||
},
|
||||
"stopStatement": "No rematerialization or validation refresh was rerun inside this bounded closure plan."
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"route": "parameter_default_semantics_hardening",
|
||||
"date": "2026-04-21",
|
||||
"status": "completed",
|
||||
"bucket": {
|
||||
"focus_archetype": "multi_mode_request",
|
||||
"anchor_scene": "sweep-030-scene",
|
||||
"source_driven_default_strategy_slice": true
|
||||
},
|
||||
"changes": {
|
||||
"source_side_default_strategy_recovered": true,
|
||||
"generated_param_resolver_config_updated": true,
|
||||
"runtime_resolver_logic_changed": false,
|
||||
"materialized_skills_changed": false
|
||||
},
|
||||
"verified": {
|
||||
"default_strategy": "lineloss_page_semantics",
|
||||
"tests": [
|
||||
"cargo test --test scene_generator_test analyzer_recovers_lineloss_period_default_strategy_from_source -- --nocapture",
|
||||
"cargo test --test scene_generator_test generator_writes_real_sweep_030_org_dictionary_from_embedded_source -- --nocapture",
|
||||
"cargo test --test scene_generator_modes_test -- --nocapture"
|
||||
]
|
||||
},
|
||||
"residuals": [
|
||||
"route only preserves first reusable default strategy slice for lineloss-style month/week pages",
|
||||
"no rematerialization or validation refresh executed in this route",
|
||||
"other period/date semantics remain for later expansion"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"route": "resolver_request_mapping_hardening",
|
||||
"date": "2026-04-20",
|
||||
"status": "completed",
|
||||
"implementedSlice": {
|
||||
"family": "multi_mode_request",
|
||||
"scope": [
|
||||
"explicit org resolver output to request-field mapping",
|
||||
"explicit period payload expansion to request-field mapping",
|
||||
"scene.toml request-mapping metadata emission",
|
||||
"generated browser-script request-body construction via mapping metadata"
|
||||
],
|
||||
"bucketReason": "highest-signal reusable parameterized request bucket"
|
||||
},
|
||||
"changedFiles": [
|
||||
"src/generated_scene/ir.rs",
|
||||
"src/generated_scene/generator.rs",
|
||||
"tests/scene_generator_test.rs",
|
||||
"tests/scene_generator_modes_test.rs"
|
||||
],
|
||||
"emittedMetadata": {
|
||||
"irField": "ModeIr.requestFieldMappings",
|
||||
"sceneTomlSection": "[[request_mappings]]",
|
||||
"mappingExamples": [
|
||||
{
|
||||
"sourceField": "org_code",
|
||||
"targetField": "orgno",
|
||||
"mode": "month"
|
||||
},
|
||||
{
|
||||
"sourceField": "period_payload.fdate",
|
||||
"targetField": "fdate",
|
||||
"mode": "month"
|
||||
},
|
||||
{
|
||||
"sourceField": "period_payload.weekSfdate",
|
||||
"targetField": "weekSfdate",
|
||||
"mode": "week"
|
||||
},
|
||||
{
|
||||
"sourceField": "period_payload.weekEfdate",
|
||||
"targetField": "weekEfdate",
|
||||
"mode": "week"
|
||||
}
|
||||
]
|
||||
},
|
||||
"scriptBehaviorDelta": {
|
||||
"before": "multi_mode_request merged all raw args into requestBody",
|
||||
"after": "multi_mode_request resolves template values, normalizes period_payload, applies explicit request mappings, and does not blindly merge resolver args"
|
||||
},
|
||||
"verification": {
|
||||
"testsPassed": [
|
||||
"cargo test --test scene_generator_test generator_derives_reusable_request_field_mappings_for_real_g2_fixture -- --nocapture",
|
||||
"cargo test --test scene_generator_test generator_writes_multi_mode_package_with_generation_report -- --nocapture",
|
||||
"cargo test --test scene_generator_modes_test -- --nocapture",
|
||||
"cargo test --test scene_generator_test generator_writes_multi_mode_package_from_deterministic_analysis -- --nocapture",
|
||||
"cargo test --test scene_generator_test generator_blocks_incomplete_multi_mode_contract -- --nocapture"
|
||||
],
|
||||
"testCount": 9
|
||||
},
|
||||
"residuals": [
|
||||
"single_request_enrichment and other non-G2 request builders still rely on pre-route request construction semantics",
|
||||
"no 102-scene rematerialization or validation refresh was run in this route",
|
||||
"runtime_url/dictionary/alias/default-semantics routes remain pending"
|
||||
],
|
||||
"nextRoute": "runtime_url_classification_hardening"
|
||||
}
|
||||
253
tests/fixtures/generated_scene/generated_scene_rule_hardening_route_clusters_2026-04-20.json
vendored
Normal file
253
tests/fixtures/generated_scene/generated_scene_rule_hardening_route_clusters_2026-04-20.json
vendored
Normal file
@@ -0,0 +1,253 @@
|
||||
{
|
||||
"runDate": "2026-04-20",
|
||||
"sourceLedger": "tests/fixtures/generated_scene/generated_scene_source_first_runtime_semantics_ledger_2026-04-20.json",
|
||||
"routeOrder": [
|
||||
"resolver_request_mapping_hardening",
|
||||
"runtime_url_classification_hardening",
|
||||
"embedded_dictionary_extraction_hardening",
|
||||
"parameter_default_semantics_recovery_hardening",
|
||||
"alias_generation_hardening"
|
||||
],
|
||||
"routeClusters": [
|
||||
{
|
||||
"route": "resolver_request_mapping_hardening",
|
||||
"count": 102,
|
||||
"highRiskCount": 76,
|
||||
"mediumRiskCount": 26,
|
||||
"archetypeCounts": [
|
||||
{
|
||||
"archetype": "paginated_enrichment",
|
||||
"count": 51
|
||||
},
|
||||
{
|
||||
"archetype": "host_bridge_workflow",
|
||||
"count": 26
|
||||
},
|
||||
{
|
||||
"archetype": "multi_mode_request",
|
||||
"count": 10
|
||||
},
|
||||
{
|
||||
"archetype": "local_doc_pipeline",
|
||||
"count": 6
|
||||
},
|
||||
{
|
||||
"archetype": "single_request_enrichment",
|
||||
"count": 5
|
||||
},
|
||||
{
|
||||
"archetype": "multi_endpoint_inventory",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"archetype": "page_state_eval",
|
||||
"count": 2
|
||||
}
|
||||
],
|
||||
"anchorScenes": [
|
||||
"sweep-002-scene",
|
||||
"sweep-003-scene",
|
||||
"sweep-004-scene",
|
||||
"sweep-005-scene",
|
||||
"sweep-006-scene",
|
||||
"sweep-007-scene",
|
||||
"sweep-008-scene",
|
||||
"sweep-009-scene",
|
||||
"sweep-010-scene",
|
||||
"sweep-011-scene"
|
||||
]
|
||||
},
|
||||
{
|
||||
"route": "runtime_url_classification_hardening",
|
||||
"count": 102,
|
||||
"highRiskCount": 76,
|
||||
"mediumRiskCount": 26,
|
||||
"archetypeCounts": [
|
||||
{
|
||||
"archetype": "paginated_enrichment",
|
||||
"count": 51
|
||||
},
|
||||
{
|
||||
"archetype": "host_bridge_workflow",
|
||||
"count": 26
|
||||
},
|
||||
{
|
||||
"archetype": "multi_mode_request",
|
||||
"count": 10
|
||||
},
|
||||
{
|
||||
"archetype": "local_doc_pipeline",
|
||||
"count": 6
|
||||
},
|
||||
{
|
||||
"archetype": "single_request_enrichment",
|
||||
"count": 5
|
||||
},
|
||||
{
|
||||
"archetype": "multi_endpoint_inventory",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"archetype": "page_state_eval",
|
||||
"count": 2
|
||||
}
|
||||
],
|
||||
"anchorScenes": [
|
||||
"sweep-002-scene",
|
||||
"sweep-003-scene",
|
||||
"sweep-004-scene",
|
||||
"sweep-005-scene",
|
||||
"sweep-006-scene",
|
||||
"sweep-007-scene",
|
||||
"sweep-008-scene",
|
||||
"sweep-009-scene",
|
||||
"sweep-010-scene",
|
||||
"sweep-011-scene"
|
||||
]
|
||||
},
|
||||
{
|
||||
"route": "embedded_dictionary_extraction_hardening",
|
||||
"count": 102,
|
||||
"highRiskCount": 76,
|
||||
"mediumRiskCount": 26,
|
||||
"archetypeCounts": [
|
||||
{
|
||||
"archetype": "paginated_enrichment",
|
||||
"count": 51
|
||||
},
|
||||
{
|
||||
"archetype": "host_bridge_workflow",
|
||||
"count": 26
|
||||
},
|
||||
{
|
||||
"archetype": "multi_mode_request",
|
||||
"count": 10
|
||||
},
|
||||
{
|
||||
"archetype": "local_doc_pipeline",
|
||||
"count": 6
|
||||
},
|
||||
{
|
||||
"archetype": "single_request_enrichment",
|
||||
"count": 5
|
||||
},
|
||||
{
|
||||
"archetype": "multi_endpoint_inventory",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"archetype": "page_state_eval",
|
||||
"count": 2
|
||||
}
|
||||
],
|
||||
"anchorScenes": [
|
||||
"sweep-002-scene",
|
||||
"sweep-003-scene",
|
||||
"sweep-004-scene",
|
||||
"sweep-005-scene",
|
||||
"sweep-006-scene",
|
||||
"sweep-007-scene",
|
||||
"sweep-008-scene",
|
||||
"sweep-009-scene",
|
||||
"sweep-010-scene",
|
||||
"sweep-011-scene"
|
||||
]
|
||||
},
|
||||
{
|
||||
"route": "parameter_default_semantics_recovery_hardening",
|
||||
"count": 89,
|
||||
"highRiskCount": 75,
|
||||
"mediumRiskCount": 14,
|
||||
"archetypeCounts": [
|
||||
{
|
||||
"archetype": "paginated_enrichment",
|
||||
"count": 45
|
||||
},
|
||||
{
|
||||
"archetype": "host_bridge_workflow",
|
||||
"count": 22
|
||||
},
|
||||
{
|
||||
"archetype": "multi_mode_request",
|
||||
"count": 9
|
||||
},
|
||||
{
|
||||
"archetype": "local_doc_pipeline",
|
||||
"count": 6
|
||||
},
|
||||
{
|
||||
"archetype": "single_request_enrichment",
|
||||
"count": 5
|
||||
},
|
||||
{
|
||||
"archetype": "page_state_eval",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"archetype": "multi_endpoint_inventory",
|
||||
"count": 1
|
||||
}
|
||||
],
|
||||
"anchorScenes": [
|
||||
"sweep-002-scene",
|
||||
"sweep-003-scene",
|
||||
"sweep-004-scene",
|
||||
"sweep-005-scene",
|
||||
"sweep-006-scene",
|
||||
"sweep-007-scene",
|
||||
"sweep-008-scene",
|
||||
"sweep-009-scene",
|
||||
"sweep-010-scene",
|
||||
"sweep-011-scene"
|
||||
]
|
||||
},
|
||||
{
|
||||
"route": "alias_generation_hardening",
|
||||
"count": 84,
|
||||
"highRiskCount": 73,
|
||||
"mediumRiskCount": 11,
|
||||
"archetypeCounts": [
|
||||
{
|
||||
"archetype": "paginated_enrichment",
|
||||
"count": 40
|
||||
},
|
||||
{
|
||||
"archetype": "host_bridge_workflow",
|
||||
"count": 24
|
||||
},
|
||||
{
|
||||
"archetype": "multi_mode_request",
|
||||
"count": 7
|
||||
},
|
||||
{
|
||||
"archetype": "local_doc_pipeline",
|
||||
"count": 6
|
||||
},
|
||||
{
|
||||
"archetype": "single_request_enrichment",
|
||||
"count": 4
|
||||
},
|
||||
{
|
||||
"archetype": "multi_endpoint_inventory",
|
||||
"count": 2
|
||||
},
|
||||
{
|
||||
"archetype": "page_state_eval",
|
||||
"count": 1
|
||||
}
|
||||
],
|
||||
"anchorScenes": [
|
||||
"sweep-002-scene",
|
||||
"sweep-003-scene",
|
||||
"sweep-004-scene",
|
||||
"sweep-005-scene",
|
||||
"sweep-006-scene",
|
||||
"sweep-007-scene",
|
||||
"sweep-008-scene",
|
||||
"sweep-009-scene",
|
||||
"sweep-010-scene",
|
||||
"sweep-011-scene"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"runDate": "2026-04-21",
|
||||
"plan": "docs/superpowers/plans/2026-04-21-generated-scene-runtime-semantics-post-refresh-residual-closure-plan.md",
|
||||
"scope": "post-refresh-residual-closure-only",
|
||||
"changedFiles": [
|
||||
"src/generated_scene/generator.rs",
|
||||
"tests/scene_generator_test.rs"
|
||||
],
|
||||
"residuals": [
|
||||
{
|
||||
"id": "deterministic_suffix_regression",
|
||||
"before": "rematerialized scene.toml emitted scene-name deterministic suffixes",
|
||||
"after": "render_scene_toml emits suffix = U+3002 x3",
|
||||
"validation": "generator_writes_real_sweep_030_org_dictionary_from_embedded_source"
|
||||
},
|
||||
{
|
||||
"id": "sweep_078_toml_corruption",
|
||||
"before": "unescaped newline/control content could be written into TOML string scalars",
|
||||
"after": "escape_toml escapes newline, carriage return, tab, quotes, backslashes, and control characters",
|
||||
"validation": "generator_escapes_request_mapping_fields_for_valid_toml"
|
||||
}
|
||||
],
|
||||
"tests": [
|
||||
{
|
||||
"command": "cargo test --test scene_generator_test generator_writes_real_sweep_030_org_dictionary_from_embedded_source -- --nocapture",
|
||||
"status": "passed"
|
||||
},
|
||||
{
|
||||
"command": "cargo test --test scene_generator_test generator_escapes_request_mapping_fields_for_valid_toml -- --nocapture",
|
||||
"status": "passed"
|
||||
}
|
||||
],
|
||||
"notExecuted": [
|
||||
"rematerialization refresh",
|
||||
"validation refresh",
|
||||
"pseudo-production execution",
|
||||
"official board update"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"summary": {
|
||||
"runDate": "2026-04-21",
|
||||
"plan": "docs/superpowers/plans/2026-04-21-generated-scene-runtime-semantics-rematerialization-execution-plan.md",
|
||||
"rerunReason": "local-doc pipeline residual closure verification",
|
||||
"outputRoot": "D:\\data\\ideaSpace\\rust\\sgClaw\\claw-new\\examples\\scene_skill_102_runtime_semantics_rematerialization_2026-04-21",
|
||||
"totalScenes": 102,
|
||||
"attempted": 102,
|
||||
"skillDirectories": 102,
|
||||
"materialized": 102,
|
||||
"failed": 0,
|
||||
"durationSeconds": 2170.2
|
||||
},
|
||||
"failures": []
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"route": "runtime_url_classification_hardening",
|
||||
"date": "2026-04-21",
|
||||
"status": "completed",
|
||||
"fixed_input_bucket": [
|
||||
"scenes_with_strong_source_evidence_for_multiple_url_roles",
|
||||
"scenes_with_target_url_only_generated_manifest",
|
||||
"high_signal_browser_script_scenes_with_runtime_context_module_route_divergence"
|
||||
],
|
||||
"implemented_slice": {
|
||||
"generator_metadata_fields": [
|
||||
"bootstrap.appEntryUrl",
|
||||
"bootstrap.moduleRouteUrl",
|
||||
"bootstrap.targetUrlKind"
|
||||
],
|
||||
"scene_toml_fields": [
|
||||
"bootstrap.app_entry_url",
|
||||
"bootstrap.module_route_url",
|
||||
"bootstrap.target_url_kind"
|
||||
],
|
||||
"structured_evidence_fields": [
|
||||
"appEntryUrl",
|
||||
"moduleRouteUrl",
|
||||
"targetUrlKind"
|
||||
],
|
||||
"generation_report_fields": [
|
||||
"App entry URL",
|
||||
"Module route URL",
|
||||
"Target URL kind"
|
||||
]
|
||||
},
|
||||
"bucketed_examples": [
|
||||
{
|
||||
"scene": "report_collection fixture",
|
||||
"runtime_context_url": "http://20.76.57.61:18080/gsllys",
|
||||
"module_route_url": "http://20.76.57.61:18080/gsllys/tqLinelossStatis/tqQualifyRateMonitor",
|
||||
"target_url_kind": "runtime_context"
|
||||
},
|
||||
{
|
||||
"scene": "multi_mode package fixture",
|
||||
"runtime_context_url": "http://20.76.57.61:18080/gsllys",
|
||||
"module_route_url": "http://20.76.57.61:18080/gsllys/monthReport",
|
||||
"target_url_kind": "runtime_context"
|
||||
}
|
||||
],
|
||||
"tests": [
|
||||
{
|
||||
"command": "cargo test --test scene_generator_test analyzer_classifies_supported_report_collection_source -- --nocapture",
|
||||
"status": "passed"
|
||||
},
|
||||
{
|
||||
"command": "cargo test --test scene_generator_test generator_writes_multi_mode_package_with_generation_report -- --nocapture",
|
||||
"status": "passed"
|
||||
},
|
||||
{
|
||||
"command": "cargo test --test scene_generator_test generator_writes_multi_mode_package_from_deterministic_analysis -- --nocapture",
|
||||
"status": "passed"
|
||||
},
|
||||
{
|
||||
"command": "cargo test --test scene_generator_modes_test -- --nocapture",
|
||||
"status": "passed"
|
||||
}
|
||||
],
|
||||
"not_done_in_route": [
|
||||
"no_full_102_rematerialization",
|
||||
"no_validation_refresh",
|
||||
"no_runtime_or_callback_host_changes",
|
||||
"no_direct_generated_skill_edits"
|
||||
]
|
||||
}
|
||||
7598
tests/fixtures/generated_scene/generated_scene_source_evidence_cross_scan_2026-04-20.json
vendored
Normal file
7598
tests/fixtures/generated_scene/generated_scene_source_evidence_cross_scan_2026-04-20.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
10621
tests/fixtures/generated_scene/generated_scene_source_first_runtime_semantics_ledger_2026-04-20.json
vendored
Normal file
10621
tests/fixtures/generated_scene/generated_scene_source_first_runtime_semantics_ledger_2026-04-20.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
52
tests/fixtures/generated_scene/host_bridge_runtime_followup_2026-04-19.json
vendored
Normal file
52
tests/fixtures/generated_scene/host_bridge_runtime_followup_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"runDate": "2026-04-19",
|
||||
"plan": "2026-04-19-host-bridge-runtime-roadmap-plan.md",
|
||||
"parentFramework": "2026-04-19-scene-skill-102-full-coverage-framework-plan.md",
|
||||
"parentSequence": "2026-04-19-final-2-residual-child-plan-sequence-plan.md",
|
||||
"fixedInputBucket": [
|
||||
"sweep-085-scene"
|
||||
],
|
||||
"officialBoardUpdated": false,
|
||||
"implementationSlice": {
|
||||
"type": "none",
|
||||
"summary": "The fixed scene closed under the current G7 multi_endpoint_inventory path; no host-bridge runtime implementation was required in this plan."
|
||||
},
|
||||
"summary": {
|
||||
"totalScenes": 1,
|
||||
"autoPass": 1,
|
||||
"failClosedKnown": 0,
|
||||
"sourceUnreadable": 0,
|
||||
"unknown": 0
|
||||
},
|
||||
"scenes": [
|
||||
{
|
||||
"sceneId": "sweep-085-scene",
|
||||
"sceneName": "计量资产库存统计",
|
||||
"previousFrameworkStatus": "framework-structured-fail-closed",
|
||||
"previousWorkflowArchetype": "host_bridge_workflow",
|
||||
"rawStatus": "auto-pass",
|
||||
"workflowArchetype": "multi_endpoint_inventory",
|
||||
"readinessLevel": "A",
|
||||
"bootstrap": {
|
||||
"expectedDomain": "yxgateway.gs.sgcc.com.cn",
|
||||
"targetUrl": "http://yxgateway.gs.sgcc.com.cn/emss-asf-assetsubjquery-front",
|
||||
"source": "deterministic"
|
||||
},
|
||||
"missingPieces": [],
|
||||
"risks": [],
|
||||
"passedGates": [
|
||||
"workflow_contract_complete",
|
||||
"runtime_contract_compatible",
|
||||
"g7_inventory_endpoints_detected",
|
||||
"g7_fail_closed"
|
||||
],
|
||||
"skillDir": "examples/host_bridge_runtime_followup_2026-04-19/skills/sweep-085-scene",
|
||||
"reportPath": "examples/host_bridge_runtime_followup_2026-04-19/skills/sweep-085-scene/references/generation-report.json"
|
||||
}
|
||||
],
|
||||
"notes": [
|
||||
"The official board already marks this scene as G7 / boundary-family / executed-pass at the real-sample layer.",
|
||||
"The framework residual was a stale host_bridge_workflow view from earlier residual follow-up assets.",
|
||||
"Official board update is deferred to final-2 board reconciliation refresh."
|
||||
]
|
||||
}
|
||||
29
tests/fixtures/generated_scene/host_bridge_runtime_reconciliation_candidates_2026-04-19.json
vendored
Normal file
29
tests/fixtures/generated_scene/host_bridge_runtime_reconciliation_candidates_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"runDate": "2026-04-19",
|
||||
"plan": "2026-04-19-host-bridge-runtime-roadmap-plan.md",
|
||||
"policySource": "tests/fixtures/generated_scene/promotion_board_reconciliation_policy_2026-04-19.json",
|
||||
"followupSource": "tests/fixtures/generated_scene/host_bridge_runtime_followup_2026-04-19.json",
|
||||
"totalScenes": 1,
|
||||
"summary": {
|
||||
"framework-auto-pass-candidate": 1,
|
||||
"framework-structured-fail-closed": 0,
|
||||
"source-unreadable": 0,
|
||||
"unresolved-followup-status": 0
|
||||
},
|
||||
"officialBoardUpdated": false,
|
||||
"canAutoUpdateBoard": false,
|
||||
"scenes": [
|
||||
{
|
||||
"sceneId": "sweep-085-scene",
|
||||
"sceneName": "计量资产库存统计",
|
||||
"rawStatus": "auto-pass",
|
||||
"reconciliationCandidateStatus": "framework-auto-pass-candidate",
|
||||
"workflowArchetype": "multi_endpoint_inventory",
|
||||
"readinessLevel": "A",
|
||||
"decisionOverlay": null,
|
||||
"nextAction": null,
|
||||
"canAutoUpdateBoard": false,
|
||||
"policyReason": "Route 6 policy requires a dedicated board reconciliation plan; host-bridge runtime roadmap only publishes candidates."
|
||||
}
|
||||
]
|
||||
}
|
||||
54
tests/fixtures/generated_scene/known_family_timeout_diagnostic_2026-04-19.json
vendored
Normal file
54
tests/fixtures/generated_scene/known_family_timeout_diagnostic_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"diagnosticDate": "2026-04-19",
|
||||
"scope": "known-family-timeout-diagnostic",
|
||||
"results": [
|
||||
{
|
||||
"sceneId": "sweep-030-scene",
|
||||
"sceneName": "台区线损大数据-月_周累计线损率统计分析",
|
||||
"elapsedSeconds": 21.69,
|
||||
"timedOut": false,
|
||||
"exitCode": 0,
|
||||
"workflowArchetype": "multi_mode_request",
|
||||
"readinessLevel": "A",
|
||||
"generationStatus": "generated",
|
||||
"diagnosticLabel": "known-family-rerun-pass",
|
||||
"stderrTail": ""
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-076-scene",
|
||||
"sceneName": "白银线损周报",
|
||||
"elapsedSeconds": 34.31,
|
||||
"timedOut": false,
|
||||
"exitCode": 0,
|
||||
"workflowArchetype": "multi_mode_request",
|
||||
"readinessLevel": "A",
|
||||
"generationStatus": "generated",
|
||||
"diagnosticLabel": "known-family-rerun-pass",
|
||||
"stderrTail": ""
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-078-scene",
|
||||
"sceneName": "线损同期差异报表",
|
||||
"elapsedSeconds": 23.36,
|
||||
"timedOut": false,
|
||||
"exitCode": 0,
|
||||
"workflowArchetype": "multi_mode_request",
|
||||
"readinessLevel": "A",
|
||||
"generationStatus": "generated",
|
||||
"diagnosticLabel": "known-family-rerun-pass",
|
||||
"stderrTail": ""
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-079-scene",
|
||||
"sceneName": "线损大数据-窃电分析",
|
||||
"elapsedSeconds": 29.15,
|
||||
"timedOut": false,
|
||||
"exitCode": 0,
|
||||
"workflowArchetype": "multi_mode_request",
|
||||
"readinessLevel": "A",
|
||||
"generationStatus": "generated",
|
||||
"diagnosticLabel": "known-family-rerun-pass",
|
||||
"stderrTail": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
156
tests/fixtures/generated_scene/local_doc_official_board_reconciliation_refresh_2026-04-19.json
vendored
Normal file
156
tests/fixtures/generated_scene/local_doc_official_board_reconciliation_refresh_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
{
|
||||
"runDate": "2026-04-19",
|
||||
"plan": "2026-04-19-local-doc-official-board-reconciliation-refresh-plan.md",
|
||||
"inputCandidates": "tests/fixtures/generated_scene/local_doc_runtime_reconciliation_candidates_2026-04-19.json",
|
||||
"officialBoard": "tests/fixtures/generated_scene/scene_execution_board_2026-04-18.json",
|
||||
"officialBoardUpdated": true,
|
||||
"updatedSceneCount": 5,
|
||||
"summary": {
|
||||
"totalScenes": 102,
|
||||
"frameworkStatusCounts": {
|
||||
"framework-auto-pass": 100,
|
||||
"framework-structured-fail-closed": 2,
|
||||
"framework-valid-host-bridge": 0,
|
||||
"source-unreadable": 0,
|
||||
"missing-source": 0,
|
||||
"unsupported-family": 0,
|
||||
"misclassified-unresolved": 0,
|
||||
"unresolved-followup-status": 0
|
||||
},
|
||||
"frameworkAutoPassCount": 100,
|
||||
"frameworkStructuredFailClosedCount": 2,
|
||||
"frameworkUnresolvedCount": 0
|
||||
},
|
||||
"updatedScenes": [
|
||||
{
|
||||
"sceneId": "sweep-033-scene",
|
||||
"officialBoardSceneName": "售电收入日统计",
|
||||
"candidateSceneName": "供电可靠率指标统计表",
|
||||
"before": {
|
||||
"currentFrameworkStatus": "framework-structured-fail-closed",
|
||||
"currentFrameworkCandidateStatus": "framework-structured-fail-closed",
|
||||
"currentFrameworkArchetype": "local_doc_pipeline",
|
||||
"currentFrameworkReadiness": "C",
|
||||
"currentFrameworkDecisionOverlay": "hold-for-local-doc-runtime-roadmap",
|
||||
"currentFrameworkNextAction": "future-local-doc-runtime-roadmap-input"
|
||||
},
|
||||
"after": {
|
||||
"currentFrameworkStatus": "framework-auto-pass",
|
||||
"currentFrameworkCandidateStatus": "framework-auto-pass-candidate",
|
||||
"currentFrameworkArchetype": "local_doc_pipeline",
|
||||
"currentFrameworkReadiness": "A",
|
||||
"currentFrameworkDecisionOverlay": null,
|
||||
"currentFrameworkNextAction": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-034-scene",
|
||||
"officialBoardSceneName": "售电收入日统计排程预测",
|
||||
"candidateSceneName": "供电可靠性数据质量自查报告月报",
|
||||
"before": {
|
||||
"currentFrameworkStatus": "framework-structured-fail-closed",
|
||||
"currentFrameworkCandidateStatus": "framework-structured-fail-closed",
|
||||
"currentFrameworkArchetype": "local_doc_pipeline",
|
||||
"currentFrameworkReadiness": "C",
|
||||
"currentFrameworkDecisionOverlay": "hold-for-local-doc-runtime-roadmap",
|
||||
"currentFrameworkNextAction": "future-local-doc-runtime-roadmap-input"
|
||||
},
|
||||
"after": {
|
||||
"currentFrameworkStatus": "framework-auto-pass",
|
||||
"currentFrameworkCandidateStatus": "framework-auto-pass-candidate",
|
||||
"currentFrameworkArchetype": "local_doc_pipeline",
|
||||
"currentFrameworkReadiness": "A",
|
||||
"currentFrameworkDecisionOverlay": null,
|
||||
"currentFrameworkNextAction": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-042-scene",
|
||||
"officialBoardSceneName": "四类主动工单统计",
|
||||
"candidateSceneName": "国网金昌供电公司营商环境周例会报告",
|
||||
"before": {
|
||||
"currentFrameworkStatus": "framework-structured-fail-closed",
|
||||
"currentFrameworkCandidateStatus": "framework-structured-fail-closed",
|
||||
"currentFrameworkArchetype": "local_doc_pipeline",
|
||||
"currentFrameworkReadiness": "C",
|
||||
"currentFrameworkDecisionOverlay": "hold-for-local-doc-runtime-roadmap",
|
||||
"currentFrameworkNextAction": "future-local-doc-runtime-roadmap-input"
|
||||
},
|
||||
"after": {
|
||||
"currentFrameworkStatus": "framework-auto-pass",
|
||||
"currentFrameworkCandidateStatus": "framework-auto-pass-candidate",
|
||||
"currentFrameworkArchetype": "local_doc_pipeline",
|
||||
"currentFrameworkReadiness": "A",
|
||||
"currentFrameworkDecisionOverlay": null,
|
||||
"currentFrameworkNextAction": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-051-scene",
|
||||
"officialBoardSceneName": "安全管控月度工作通报",
|
||||
"candidateSceneName": "嘉峪关可靠性分析报告",
|
||||
"before": {
|
||||
"currentFrameworkStatus": "framework-structured-fail-closed",
|
||||
"currentFrameworkCandidateStatus": "framework-structured-fail-closed",
|
||||
"currentFrameworkArchetype": "local_doc_pipeline",
|
||||
"currentFrameworkReadiness": "C",
|
||||
"currentFrameworkDecisionOverlay": "hold-for-local-doc-runtime-roadmap",
|
||||
"currentFrameworkNextAction": "future-local-doc-runtime-roadmap-input"
|
||||
},
|
||||
"after": {
|
||||
"currentFrameworkStatus": "framework-auto-pass",
|
||||
"currentFrameworkCandidateStatus": "framework-auto-pass-candidate",
|
||||
"currentFrameworkArchetype": "local_doc_pipeline",
|
||||
"currentFrameworkReadiness": "A",
|
||||
"currentFrameworkDecisionOverlay": null,
|
||||
"currentFrameworkNextAction": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-074-scene",
|
||||
"officialBoardSceneName": "白银公司指挥中心供电服务业务日报",
|
||||
"candidateSceneName": "同兴智能安全督查日报",
|
||||
"before": {
|
||||
"currentFrameworkStatus": "framework-structured-fail-closed",
|
||||
"currentFrameworkCandidateStatus": "framework-structured-fail-closed",
|
||||
"currentFrameworkArchetype": "local_doc_pipeline",
|
||||
"currentFrameworkReadiness": "C",
|
||||
"currentFrameworkDecisionOverlay": "hold-for-local-doc-runtime-roadmap",
|
||||
"currentFrameworkNextAction": "future-local-doc-runtime-roadmap-input"
|
||||
},
|
||||
"after": {
|
||||
"currentFrameworkStatus": "framework-auto-pass",
|
||||
"currentFrameworkCandidateStatus": "framework-auto-pass-candidate",
|
||||
"currentFrameworkArchetype": "local_doc_pipeline",
|
||||
"currentFrameworkReadiness": "A",
|
||||
"currentFrameworkDecisionOverlay": null,
|
||||
"currentFrameworkNextAction": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"remainingStructuredFailClosed": [
|
||||
{
|
||||
"sceneId": "sweep-085-scene",
|
||||
"sceneName": "计量资产库存统计",
|
||||
"currentFrameworkStatus": "framework-structured-fail-closed",
|
||||
"currentFrameworkArchetype": "host_bridge_workflow",
|
||||
"currentFrameworkReadiness": "C",
|
||||
"currentFrameworkDecisionOverlay": "hold-for-host-bridge-runtime-roadmap",
|
||||
"currentFrameworkNextAction": "future-host-bridge-runtime-roadmap-input"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-091-scene",
|
||||
"sceneName": "配网异常设备监控统计",
|
||||
"currentFrameworkStatus": "framework-structured-fail-closed",
|
||||
"currentFrameworkArchetype": "page_state_eval",
|
||||
"currentFrameworkReadiness": "C",
|
||||
"currentFrameworkDecisionOverlay": "isolate-bootstrap-target-residual",
|
||||
"currentFrameworkNextAction": "future-bootstrap-target-normalization-roadmap-input"
|
||||
}
|
||||
],
|
||||
"notes": [
|
||||
"Only framework-layer fields were updated for the five fixed local-doc scenes.",
|
||||
"Workbook snapshot fields, official scene names, currentGroup/currentStatus, and real-sample fields were preserved.",
|
||||
"Official-board names for these sweep ids still differ from local-doc candidate source names; this refresh intentionally does not rename board scenes."
|
||||
]
|
||||
}
|
||||
77
tests/fixtures/generated_scene/local_doc_runtime_reconciliation_candidates_2026-04-19.json
vendored
Normal file
77
tests/fixtures/generated_scene/local_doc_runtime_reconciliation_candidates_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"runDate": "2026-04-19",
|
||||
"plan": "2026-04-19-local-doc-runtime-roadmap-plan.md",
|
||||
"policySource": "tests/fixtures/generated_scene/promotion_board_reconciliation_policy_2026-04-19.json",
|
||||
"followupSource": "tests/fixtures/generated_scene/local_doc_runtime_roadmap_followup_2026-04-19.json",
|
||||
"totalScenes": 5,
|
||||
"summary": {
|
||||
"framework-auto-pass-candidate": 5,
|
||||
"framework-structured-fail-closed": 0,
|
||||
"source-unreadable": 0,
|
||||
"unresolved-followup-status": 0
|
||||
},
|
||||
"officialBoardUpdated": false,
|
||||
"canAutoUpdateBoard": false,
|
||||
"scenes": [
|
||||
{
|
||||
"sceneId": "sweep-033-scene",
|
||||
"sceneName": "供电可靠率指标统计表",
|
||||
"rawStatus": "auto-pass",
|
||||
"reconciliationCandidateStatus": "framework-auto-pass-candidate",
|
||||
"workflowArchetype": "local_doc_pipeline",
|
||||
"readinessLevel": "A",
|
||||
"decisionOverlay": null,
|
||||
"nextAction": null,
|
||||
"canAutoUpdateBoard": false,
|
||||
"policyReason": "Route 6 policy requires a dedicated board reconciliation plan; local-doc roadmap only publishes candidates."
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-034-scene",
|
||||
"sceneName": "供电可靠性数据质量自查报告月报",
|
||||
"rawStatus": "auto-pass",
|
||||
"reconciliationCandidateStatus": "framework-auto-pass-candidate",
|
||||
"workflowArchetype": "local_doc_pipeline",
|
||||
"readinessLevel": "A",
|
||||
"decisionOverlay": null,
|
||||
"nextAction": null,
|
||||
"canAutoUpdateBoard": false,
|
||||
"policyReason": "Route 6 policy requires a dedicated board reconciliation plan; local-doc roadmap only publishes candidates."
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-042-scene",
|
||||
"sceneName": "国网金昌供电公司营商环境周例会报告",
|
||||
"rawStatus": "auto-pass",
|
||||
"reconciliationCandidateStatus": "framework-auto-pass-candidate",
|
||||
"workflowArchetype": "local_doc_pipeline",
|
||||
"readinessLevel": "A",
|
||||
"decisionOverlay": null,
|
||||
"nextAction": null,
|
||||
"canAutoUpdateBoard": false,
|
||||
"policyReason": "Route 6 policy requires a dedicated board reconciliation plan; local-doc roadmap only publishes candidates."
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-051-scene",
|
||||
"sceneName": "嘉峪关可靠性分析报告",
|
||||
"rawStatus": "auto-pass",
|
||||
"reconciliationCandidateStatus": "framework-auto-pass-candidate",
|
||||
"workflowArchetype": "local_doc_pipeline",
|
||||
"readinessLevel": "A",
|
||||
"decisionOverlay": null,
|
||||
"nextAction": null,
|
||||
"canAutoUpdateBoard": false,
|
||||
"policyReason": "Route 6 policy requires a dedicated board reconciliation plan; local-doc roadmap only publishes candidates."
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-074-scene",
|
||||
"sceneName": "同兴智能安全督查日报",
|
||||
"rawStatus": "auto-pass",
|
||||
"reconciliationCandidateStatus": "framework-auto-pass-candidate",
|
||||
"workflowArchetype": "local_doc_pipeline",
|
||||
"readinessLevel": "A",
|
||||
"decisionOverlay": null,
|
||||
"nextAction": null,
|
||||
"canAutoUpdateBoard": false,
|
||||
"policyReason": "Route 6 policy requires a dedicated board reconciliation plan; local-doc roadmap only publishes candidates."
|
||||
}
|
||||
]
|
||||
}
|
||||
161
tests/fixtures/generated_scene/local_doc_runtime_roadmap_followup_2026-04-19.json
vendored
Normal file
161
tests/fixtures/generated_scene/local_doc_runtime_roadmap_followup_2026-04-19.json
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
{
|
||||
"runDate": "2026-04-19",
|
||||
"plan": "2026-04-19-local-doc-runtime-roadmap-plan.md",
|
||||
"parentDecision": "2026-04-19-residual-runtime-roadmap-prioritization-plan.md",
|
||||
"parentFramework": "2026-04-19-scene-skill-102-full-coverage-framework-plan.md",
|
||||
"fixedInputBucket": [
|
||||
"sweep-033-scene",
|
||||
"sweep-034-scene",
|
||||
"sweep-042-scene",
|
||||
"sweep-051-scene",
|
||||
"sweep-074-scene"
|
||||
],
|
||||
"officialBoardUpdated": false,
|
||||
"implementationSlice": {
|
||||
"type": "bounded-local-doc-contract-widening",
|
||||
"changedFiles": [
|
||||
"src/generated_scene/generator.rs",
|
||||
"tests/scene_generator_test.rs"
|
||||
],
|
||||
"summary": "Accept local_doc_pipeline selectData/configServices selectData steps as the query leg of the minimal G8 contract while still requiring doc_export and localhost runtime evidence."
|
||||
},
|
||||
"summary": {
|
||||
"totalScenes": 5,
|
||||
"autoPass": 5,
|
||||
"failClosedKnown": 0,
|
||||
"sourceUnreadable": 0,
|
||||
"unknown": 0
|
||||
},
|
||||
"scenes": [
|
||||
{
|
||||
"sceneId": "sweep-033-scene",
|
||||
"sceneName": "供电可靠率指标统计表",
|
||||
"workflowArchetype": "local_doc_pipeline",
|
||||
"readinessLevel": "A",
|
||||
"missingPieces": [],
|
||||
"riskCount": 0,
|
||||
"gates": [
|
||||
{
|
||||
"name": "workflow_contract_complete",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"name": "g8_local_doc_pipeline_detected",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"name": "g8_fail_closed",
|
||||
"passed": true
|
||||
}
|
||||
],
|
||||
"rawStatus": "auto-pass",
|
||||
"skillDir": "examples/local_doc_runtime_roadmap_followup_2026-04-19/skills/sweep-033-scene",
|
||||
"reportPath": "examples/local_doc_runtime_roadmap_followup_2026-04-19/skills/sweep-033-scene/references/generation-report.json"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-034-scene",
|
||||
"sceneName": "供电可靠性数据质量自查报告月报",
|
||||
"workflowArchetype": "local_doc_pipeline",
|
||||
"readinessLevel": "A",
|
||||
"missingPieces": [],
|
||||
"riskCount": 0,
|
||||
"gates": [
|
||||
{
|
||||
"name": "workflow_contract_complete",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"name": "g8_local_doc_pipeline_detected",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"name": "g8_fail_closed",
|
||||
"passed": true
|
||||
}
|
||||
],
|
||||
"rawStatus": "auto-pass",
|
||||
"skillDir": "examples/local_doc_runtime_roadmap_followup_2026-04-19/skills/sweep-034-scene",
|
||||
"reportPath": "examples/local_doc_runtime_roadmap_followup_2026-04-19/skills/sweep-034-scene/references/generation-report.json"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-042-scene",
|
||||
"sceneName": "国网金昌供电公司营商环境周例会报告",
|
||||
"workflowArchetype": "local_doc_pipeline",
|
||||
"readinessLevel": "A",
|
||||
"missingPieces": [],
|
||||
"riskCount": 0,
|
||||
"gates": [
|
||||
{
|
||||
"name": "workflow_contract_complete",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"name": "g8_local_doc_pipeline_detected",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"name": "g8_fail_closed",
|
||||
"passed": true
|
||||
}
|
||||
],
|
||||
"rawStatus": "auto-pass",
|
||||
"skillDir": "examples/local_doc_runtime_roadmap_followup_2026-04-19/skills/sweep-042-scene",
|
||||
"reportPath": "examples/local_doc_runtime_roadmap_followup_2026-04-19/skills/sweep-042-scene/references/generation-report.json"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-051-scene",
|
||||
"sceneName": "嘉峪关可靠性分析报告",
|
||||
"workflowArchetype": "local_doc_pipeline",
|
||||
"readinessLevel": "A",
|
||||
"missingPieces": [],
|
||||
"riskCount": 0,
|
||||
"gates": [
|
||||
{
|
||||
"name": "workflow_contract_complete",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"name": "g8_local_doc_pipeline_detected",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"name": "g8_fail_closed",
|
||||
"passed": true
|
||||
}
|
||||
],
|
||||
"rawStatus": "auto-pass",
|
||||
"skillDir": "examples/local_doc_runtime_roadmap_followup_2026-04-19/skills/sweep-051-scene",
|
||||
"reportPath": "examples/local_doc_runtime_roadmap_followup_2026-04-19/skills/sweep-051-scene/references/generation-report.json"
|
||||
},
|
||||
{
|
||||
"sceneId": "sweep-074-scene",
|
||||
"sceneName": "同兴智能安全督查日报",
|
||||
"workflowArchetype": "local_doc_pipeline",
|
||||
"readinessLevel": "A",
|
||||
"missingPieces": [],
|
||||
"riskCount": 0,
|
||||
"gates": [
|
||||
{
|
||||
"name": "workflow_contract_complete",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"name": "g8_local_doc_pipeline_detected",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"name": "g8_fail_closed",
|
||||
"passed": true
|
||||
}
|
||||
],
|
||||
"rawStatus": "auto-pass",
|
||||
"skillDir": "examples/local_doc_runtime_roadmap_followup_2026-04-19/skills/sweep-074-scene",
|
||||
"reportPath": "examples/local_doc_runtime_roadmap_followup_2026-04-19/skills/sweep-074-scene/references/generation-report.json"
|
||||
}
|
||||
],
|
||||
"notes": [
|
||||
"This asset records only the fixed five local-doc roadmap scenes.",
|
||||
"The official execution board is intentionally not updated in this plan.",
|
||||
"Official-board scene names for the same sweep ids differ from the plan-target source names; this plan keeps fixed scene ids and does not correct board naming."
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"route": "monitoring-action-detect-preview-generator-implementation",
|
||||
"date": "2026-04-21",
|
||||
"status": "implemented-preview-only",
|
||||
"samplePackage": "examples/monitoring_action_detect_preview_anchor_2026-04-21/skills/command-center-fee-control-monitor",
|
||||
"family": "monitoring_action_workflow",
|
||||
"mode": "detect_preview",
|
||||
"sideEffectsExecutable": false,
|
||||
"blockedSignalsPresentAsData": [
|
||||
"repetCtrlSend",
|
||||
"mac.sendMessages",
|
||||
"mac.callOutLogin",
|
||||
"mac.audioPlay",
|
||||
"_this.autoTask",
|
||||
"_this.processQueue",
|
||||
"mac.exeTQueue"
|
||||
],
|
||||
"scriptSafetyScan": {
|
||||
"containsExecutableRepetCtrlSend": false,
|
||||
"containsExecutableSendMessages": false,
|
||||
"containsExecutableCallOutLogin": false,
|
||||
"containsExecutableAudioPlay": false,
|
||||
"containsExecutableExeTQueue": false
|
||||
},
|
||||
"tests": [
|
||||
"cargo test --test scene_generator_test generator_emits_monitoring_action_detect_preview_anchor_package -- --nocapture",
|
||||
"cargo test --test scene_generator_test generator_emits_monitoring_template -- --nocapture",
|
||||
"cargo test --test scene_generator_modes_test -- --nocapture",
|
||||
"node examples/monitoring_action_detect_preview_anchor_2026-04-21/skills/command-center-fee-control-monitor/scripts/detect_preview.test.js"
|
||||
],
|
||||
"nextRecommendedRoute": "monitoring-action-mock-validation-plan"
|
||||
}
|
||||
386
tests/fixtures/generated_scene/monitoring_action_ir_contract_2026-04-21.json
vendored
Normal file
386
tests/fixtures/generated_scene/monitoring_action_ir_contract_2026-04-21.json
vendored
Normal file
@@ -0,0 +1,386 @@
|
||||
{
|
||||
"family": "monitoring_action_workflow",
|
||||
"date": "2026-04-21",
|
||||
"status": "contract-defined",
|
||||
"workflowId": "command_center_fee_control_monitoring_action",
|
||||
"displayName": "???????????????",
|
||||
"anchorEvidence": "tests/fixtures/generated_scene/monitoring_action_source_evidence_extraction_2026-04-21.json",
|
||||
"defaultMode": "detect_preview",
|
||||
"workflowStages": [
|
||||
"detect",
|
||||
"decide",
|
||||
"preview",
|
||||
"act",
|
||||
"notify",
|
||||
"log",
|
||||
"queue_next"
|
||||
],
|
||||
"mvpAllowedStages": [
|
||||
"detect",
|
||||
"decide",
|
||||
"preview"
|
||||
],
|
||||
"blockedByDefaultStages": [
|
||||
"act",
|
||||
"notify",
|
||||
"log.write",
|
||||
"queue_next"
|
||||
],
|
||||
"runtimeContext": {
|
||||
"runtime_context_url": "http://yx.gs.sgcc.com.cn/",
|
||||
"expected_domain": "yx.gs.sgcc.com.cn",
|
||||
"gateway_domain": "yxgateway.gs.sgcc.com.cn",
|
||||
"localhost_service_base": "http://localhost:13313",
|
||||
"browserAttachedRequired": true,
|
||||
"hostBridgeRequired": true
|
||||
},
|
||||
"modes": [
|
||||
{
|
||||
"name": "detect_preview",
|
||||
"enabledInMvp": true,
|
||||
"allowedStages": [
|
||||
"detect",
|
||||
"decide",
|
||||
"preview"
|
||||
],
|
||||
"forbiddenStages": [
|
||||
"act",
|
||||
"notify",
|
||||
"log.write",
|
||||
"queue_next"
|
||||
],
|
||||
"description": "Read source data and local state, compute pending/notify candidates, and output preview without executing side effects."
|
||||
},
|
||||
{
|
||||
"name": "action_plan",
|
||||
"enabledInMvp": false,
|
||||
"requiresFutureGate": "preview_approval",
|
||||
"description": "Prepare but do not execute dispatch/notify actions."
|
||||
},
|
||||
{
|
||||
"name": "execute_dispatch_confirmed",
|
||||
"enabledInMvp": false,
|
||||
"requiresFutureGate": "explicit_confirm_dispatch",
|
||||
"description": "Future mode only. Dispatch fee-control exception orders after explicit confirmation and max item limit."
|
||||
},
|
||||
{
|
||||
"name": "execute_notify_confirmed",
|
||||
"enabledInMvp": false,
|
||||
"requiresFutureGate": "explicit_confirm_notify",
|
||||
"description": "Future mode only. Send SMS/call/audio notifications after explicit confirmation and max item limit."
|
||||
}
|
||||
],
|
||||
"localStorageReads": [
|
||||
{
|
||||
"key": "loginUserInfo",
|
||||
"usage": "current user org and login context"
|
||||
},
|
||||
{
|
||||
"key": "markToken",
|
||||
"usage": "business gateway Authorization token"
|
||||
},
|
||||
{
|
||||
"key": "yxClassList",
|
||||
"usage": "cached organization tree/list"
|
||||
},
|
||||
{
|
||||
"key": "zhzxFkycSendTime",
|
||||
"usage": "last fee-control send/monitor time"
|
||||
}
|
||||
],
|
||||
"localServiceDependencies": [
|
||||
{
|
||||
"url": "http://localhost:13313/MonitorServices/getMonitorLog",
|
||||
"classification": "read_state"
|
||||
},
|
||||
{
|
||||
"url": "http://localhost:13313/MonitorServices/setMonitorData",
|
||||
"classification": "write_monitor_state",
|
||||
"sideEffect": true
|
||||
},
|
||||
{
|
||||
"url": "http://localhost:13313/MonitorServices/setMonitorLog",
|
||||
"classification": "write_monitor_log",
|
||||
"sideEffect": true
|
||||
},
|
||||
{
|
||||
"url": "http://localhost:13313/MonitorServices/setDisposeLog",
|
||||
"classification": "write_dispose_log",
|
||||
"sideEffect": true
|
||||
},
|
||||
{
|
||||
"url": "http://localhost:13313/MonitorServices/setAudioPlayLog",
|
||||
"classification": "write_notification_log",
|
||||
"sideEffect": true
|
||||
},
|
||||
{
|
||||
"url": "http://localhost:13313/MonitorServices/setSendMessageLog",
|
||||
"classification": "write_notification_log",
|
||||
"sideEffect": true
|
||||
},
|
||||
{
|
||||
"url": "http://localhost:13313/marketingServices/getOtherIphones",
|
||||
"classification": "configuration_read"
|
||||
},
|
||||
{
|
||||
"url": "http://localhost:13313/marketingServices/messageLogInfo",
|
||||
"classification": "write_notification_log",
|
||||
"sideEffect": true
|
||||
},
|
||||
{
|
||||
"url": "http://localhost:13313/marketingServices/iphonesLogInfo",
|
||||
"classification": "write_notification_log",
|
||||
"sideEffect": true
|
||||
}
|
||||
],
|
||||
"businessApiDependencies": [
|
||||
{
|
||||
"url": "http://yxgateway.gs.sgcc.com.cn/emss-cmc-authdata-subdomain/member/mgtOrg/getAllSubMgtOrgTreeByOrgCode",
|
||||
"classification": "read_org_tree"
|
||||
},
|
||||
{
|
||||
"url": "http://yxgateway.gs.sgcc.com.cn/emss-chargacctgf-paysrv-front/member/acctabnor/queryAbnorList",
|
||||
"classification": "read_exception_orders"
|
||||
},
|
||||
{
|
||||
"url": "http://yxgateway.gs.sgcc.com.cn/emss-custmgtf-custview-front//member/electrivity/queryHistoryEnergyCharge",
|
||||
"classification": "read_charge_history"
|
||||
},
|
||||
{
|
||||
"url": "http://yxgateway.gs.sgcc.com.cn/emss-chargacctgf-paysrv-front/member/acctabnor/repetCtrlSend",
|
||||
"classification": "dispatch_exception_order",
|
||||
"sideEffect": true,
|
||||
"blockedByDefault": true
|
||||
}
|
||||
],
|
||||
"stateDependencies": [
|
||||
{
|
||||
"name": "previous_monitor_log",
|
||||
"source": "MonitorServices/getMonitorLog"
|
||||
},
|
||||
{
|
||||
"name": "current_user_org",
|
||||
"source": "localStorage.loginUserInfo"
|
||||
},
|
||||
{
|
||||
"name": "gateway_token",
|
||||
"source": "localStorage.markToken"
|
||||
},
|
||||
{
|
||||
"name": "cached_org_tree",
|
||||
"source": "localStorage.yxClassList"
|
||||
},
|
||||
{
|
||||
"name": "last_fee_control_send_time",
|
||||
"source": "localStorage.zhzxFkycSendTime"
|
||||
},
|
||||
{
|
||||
"name": "phone_and_holiday_configuration",
|
||||
"source": "marketingServices/getOtherIphones"
|
||||
}
|
||||
],
|
||||
"queueDependencies": [
|
||||
{
|
||||
"name": "pendingList",
|
||||
"source": "queueObj.pendingList / obj.pendingList"
|
||||
},
|
||||
{
|
||||
"name": "autoTask",
|
||||
"source": "_this.autoTask",
|
||||
"blockedByDefault": true
|
||||
},
|
||||
{
|
||||
"name": "processQueue",
|
||||
"source": "_this.processQueue",
|
||||
"blockedByDefault": true
|
||||
},
|
||||
{
|
||||
"name": "exeTQueue",
|
||||
"source": "mac.exeTQueue",
|
||||
"blockedByDefault": true
|
||||
}
|
||||
],
|
||||
"dependencyClassification": {
|
||||
"detectPreviewAllowed": [
|
||||
"read_state",
|
||||
"configuration_read",
|
||||
"read_org_tree",
|
||||
"read_exception_orders",
|
||||
"read_charge_history"
|
||||
],
|
||||
"blockedByDefault": [
|
||||
"write_monitor_state",
|
||||
"write_monitor_log",
|
||||
"write_dispose_log",
|
||||
"write_notification_log",
|
||||
"dispatch_exception_order",
|
||||
"business_dispatch",
|
||||
"host_notify",
|
||||
"queue_continue"
|
||||
]
|
||||
},
|
||||
"decisionRules": {
|
||||
"source": "anchor scripts",
|
||||
"recoveredAsOpaqueRules": true,
|
||||
"knownInputs": [
|
||||
"previous_monitor_log",
|
||||
"current exception orders",
|
||||
"charge history",
|
||||
"phone/holiday configuration",
|
||||
"last fee-control send time"
|
||||
],
|
||||
"knownOutputs": [
|
||||
"pendingList",
|
||||
"notifyCandidates",
|
||||
"noActionList",
|
||||
"summary"
|
||||
]
|
||||
},
|
||||
"previewSchema": {
|
||||
"summary": "object",
|
||||
"pendingList": "array",
|
||||
"notifyCandidates": "array",
|
||||
"actionPlan": "array",
|
||||
"blockedSideEffects": "array",
|
||||
"evidence": "object",
|
||||
"warnings": "array"
|
||||
},
|
||||
"actionPlanSchema": {
|
||||
"itemId": "string",
|
||||
"customerNo": "string?",
|
||||
"orgNo": "string?",
|
||||
"actionType": "dispatch|sms|call|audio|log|queue_next",
|
||||
"targetEndpointOrHostCall": "string",
|
||||
"requiresConfirmation": "boolean",
|
||||
"blockedByDefault": "boolean",
|
||||
"reason": "string"
|
||||
},
|
||||
"sideEffectPolicy": {
|
||||
"dryRunDefault": true,
|
||||
"requiresExplicitConfirmation": true,
|
||||
"previewBeforeAction": true,
|
||||
"maxItemsRequiredForActionModes": true,
|
||||
"auditRecordRequired": true,
|
||||
"blockedCallSignatures": [
|
||||
"repetCtrlSend",
|
||||
"mac.sendMessages",
|
||||
"mac.callOutLogin",
|
||||
"mac.audioPlay",
|
||||
"_this.autoTask",
|
||||
"_this.processQueue",
|
||||
"mac.exeTQueue",
|
||||
"setDisposeLog",
|
||||
"setMonitorData",
|
||||
"setMonitorLog",
|
||||
"setSendMessageLog",
|
||||
"setAudioPlayLog"
|
||||
],
|
||||
"blockedActions": [
|
||||
{
|
||||
"action": "dispatch_fee_control_exception_order",
|
||||
"signals": [
|
||||
"repetCtrlSend"
|
||||
],
|
||||
"blockedByDefault": true,
|
||||
"requiredFutureGate": "explicit_confirm_dispatch"
|
||||
},
|
||||
{
|
||||
"action": "send_sms",
|
||||
"signals": [
|
||||
"mac.sendMessages"
|
||||
],
|
||||
"blockedByDefault": true,
|
||||
"requiredFutureGate": "explicit_confirm_notify"
|
||||
},
|
||||
{
|
||||
"action": "call_phone",
|
||||
"signals": [
|
||||
"mac.callOutLogin"
|
||||
],
|
||||
"blockedByDefault": true,
|
||||
"requiredFutureGate": "explicit_confirm_notify"
|
||||
},
|
||||
{
|
||||
"action": "play_audio",
|
||||
"signals": [
|
||||
"mac.audioPlay or audio log path"
|
||||
],
|
||||
"blockedByDefault": true,
|
||||
"requiredFutureGate": "explicit_confirm_notify"
|
||||
},
|
||||
{
|
||||
"action": "write_monitor_state",
|
||||
"signals": [
|
||||
"setMonitorData",
|
||||
"setMonitorLog"
|
||||
],
|
||||
"blockedByDefault": true,
|
||||
"requiredFutureGate": "dry_run_or_explicit_monitor_write"
|
||||
},
|
||||
{
|
||||
"action": "write_dispose_log",
|
||||
"signals": [
|
||||
"setDisposeLog"
|
||||
],
|
||||
"blockedByDefault": true,
|
||||
"requiredFutureGate": "explicit_confirm_dispatch"
|
||||
},
|
||||
{
|
||||
"action": "continue_queue",
|
||||
"signals": [
|
||||
"_this.autoTask",
|
||||
"_this.processQueue",
|
||||
"mac.exeTQueue"
|
||||
],
|
||||
"blockedByDefault": true,
|
||||
"requiredFutureGate": "explicit_confirm_queue_next"
|
||||
}
|
||||
]
|
||||
},
|
||||
"gateRequirements": [
|
||||
{
|
||||
"gate": "explicit_confirm_dispatch",
|
||||
"requiredFor": [
|
||||
"repetCtrlSend",
|
||||
"setDisposeLog"
|
||||
],
|
||||
"mvpEnabled": false
|
||||
},
|
||||
{
|
||||
"gate": "explicit_confirm_notify",
|
||||
"requiredFor": [
|
||||
"mac.sendMessages",
|
||||
"mac.callOutLogin",
|
||||
"mac.audioPlay",
|
||||
"notification logs"
|
||||
],
|
||||
"mvpEnabled": false
|
||||
},
|
||||
{
|
||||
"gate": "explicit_confirm_queue_next",
|
||||
"requiredFor": [
|
||||
"_this.autoTask",
|
||||
"_this.processQueue",
|
||||
"mac.exeTQueue"
|
||||
],
|
||||
"mvpEnabled": false
|
||||
},
|
||||
{
|
||||
"gate": "dry_run_or_explicit_monitor_write",
|
||||
"requiredFor": [
|
||||
"setMonitorData",
|
||||
"setMonitorLog"
|
||||
],
|
||||
"mvpEnabled": false
|
||||
}
|
||||
],
|
||||
"auditRequirements": {
|
||||
"recordResolvedMode": true,
|
||||
"recordInputFilters": true,
|
||||
"recordReadDependencies": true,
|
||||
"recordBlockedSideEffects": true,
|
||||
"recordPreviewBeforeAnyAction": true,
|
||||
"recordConfirmationForFutureActionModes": true
|
||||
},
|
||||
"nextRecommendedRoute": "monitoring-action-detect-preview-generator-design"
|
||||
}
|
||||
62
tests/fixtures/generated_scene/monitoring_action_mock_validation_fixtures_2026-04-22.json
vendored
Normal file
62
tests/fixtures/generated_scene/monitoring_action_mock_validation_fixtures_2026-04-22.json
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"queryAbnorList": [
|
||||
{
|
||||
"id": "MOCK-ORDER-001",
|
||||
"consNo": "MOCK-CONS-001",
|
||||
"custNo": "MOCK-CUST-001",
|
||||
"orgNo": "MOCK-ORG-001",
|
||||
"abnorType": "fee_control_exception",
|
||||
"createdAt": "2026-04-22T09:00:00+08:00"
|
||||
},
|
||||
{
|
||||
"id": "MOCK-ORDER-002",
|
||||
"consNo": "MOCK-CONS-002",
|
||||
"custNo": "MOCK-CUST-002",
|
||||
"orgNo": "MOCK-ORG-001",
|
||||
"abnorType": "fee_control_exception",
|
||||
"createdAt": "2026-04-22T09:10:00+08:00"
|
||||
}
|
||||
],
|
||||
"queryHistoryEnergyCharge": [
|
||||
{
|
||||
"consNo": "MOCK-CONS-001",
|
||||
"arrears": "120.00",
|
||||
"chargeStatus": "pending"
|
||||
},
|
||||
{
|
||||
"consNo": "MOCK-CONS-002",
|
||||
"arrears": "0.00",
|
||||
"chargeStatus": "cleared"
|
||||
}
|
||||
],
|
||||
"getMonitorLog": {
|
||||
"lastRunAt": "2026-04-22T08:00:00+08:00",
|
||||
"processedOrderIds": [
|
||||
"MOCK-ORDER-000"
|
||||
]
|
||||
},
|
||||
"getOtherIphones": {
|
||||
"phones": [
|
||||
"13800000000",
|
||||
"13900000000"
|
||||
],
|
||||
"holidayMode": false
|
||||
},
|
||||
"pendingList": [
|
||||
{
|
||||
"id": "MOCK-ORDER-001",
|
||||
"consNo": "MOCK-CONS-001",
|
||||
"custNo": "MOCK-CUST-001",
|
||||
"orgNo": "MOCK-ORG-001",
|
||||
"reason": "mock fee-control exception"
|
||||
}
|
||||
],
|
||||
"notifyCandidates": [
|
||||
{
|
||||
"id": "MOCK-ORDER-001",
|
||||
"channel": "sms",
|
||||
"phone": "13800000000",
|
||||
"reason": "mock notification candidate"
|
||||
}
|
||||
]
|
||||
}
|
||||
34
tests/fixtures/generated_scene/monitoring_action_mock_validation_results_2026-04-22.json
vendored
Normal file
34
tests/fixtures/generated_scene/monitoring_action_mock_validation_results_2026-04-22.json
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"status": "mock-validation-pass",
|
||||
"family": "monitoring_action_workflow",
|
||||
"mode": "detect_preview",
|
||||
"artifactStatus": "preview-ok",
|
||||
"pendingCount": 1,
|
||||
"notifyCount": 1,
|
||||
"actionPlanCount": 1,
|
||||
"blockedCallSignatures": [
|
||||
"repetCtrlSend",
|
||||
"mac.sendMessages",
|
||||
"mac.callOutLogin",
|
||||
"mac.audioPlay",
|
||||
"_this.autoTask",
|
||||
"_this.processQueue",
|
||||
"mac.exeTQueue",
|
||||
"setDisposeLog",
|
||||
"setMonitorData",
|
||||
"setMonitorLog",
|
||||
"setSendMessageLog",
|
||||
"setAudioPlayLog"
|
||||
],
|
||||
"sideEffectCounters": {
|
||||
"repetCtrlSend": 0,
|
||||
"sendMessages": 0,
|
||||
"callOutLogin": 0,
|
||||
"audioPlay": 0,
|
||||
"exeTQueue": 0,
|
||||
"productionLogWrite": 0
|
||||
},
|
||||
"nonPreviewModeBlocked": true,
|
||||
"fixtures": "tests/fixtures/generated_scene/monitoring_action_mock_validation_fixtures_2026-04-22.json",
|
||||
"skillRoot": "examples/monitoring_action_detect_preview_anchor_2026-04-21/skills/command-center-fee-control-monitor"
|
||||
}
|
||||
335
tests/fixtures/generated_scene/monitoring_action_source_evidence_extraction_2026-04-21.json
vendored
Normal file
335
tests/fixtures/generated_scene/monitoring_action_source_evidence_extraction_2026-04-21.json
vendored
Normal file
@@ -0,0 +1,335 @@
|
||||
{
|
||||
"family": "monitoring_action_workflow",
|
||||
"date": "2026-04-21",
|
||||
"status": "evidence-extracted",
|
||||
"anchorSources": [
|
||||
"D:/desk/?????/??????????.txt",
|
||||
"D:/desk/?????/?????????????.txt"
|
||||
],
|
||||
"scripts": [
|
||||
{
|
||||
"role": "detection_business_script",
|
||||
"path": "D:/desk/?????/??????????.txt",
|
||||
"observedStages": [
|
||||
"detect",
|
||||
"decide",
|
||||
"preview",
|
||||
"log",
|
||||
"notify",
|
||||
"queue_next"
|
||||
],
|
||||
"description": "Collects fee-control exception orders, enriches charge history, compares monitor state, prepares pending/notification candidates, writes monitor logs, and may enqueue follow-up actions."
|
||||
},
|
||||
{
|
||||
"role": "automation_action_script",
|
||||
"path": "D:/desk/?????/?????????????.txt",
|
||||
"observedStages": [
|
||||
"act",
|
||||
"log",
|
||||
"notify",
|
||||
"queue_next"
|
||||
],
|
||||
"description": "Consumes pendingList, dispatches exception orders through repetCtrlSend, writes dispose logs, and continues an external queue."
|
||||
}
|
||||
],
|
||||
"runtimeContext": {
|
||||
"runtime_context_url": "http://yx.gs.sgcc.com.cn/",
|
||||
"expected_domain": "yx.gs.sgcc.com.cn",
|
||||
"gateway_domain": "yxgateway.gs.sgcc.com.cn",
|
||||
"localhost_service_base": "http://localhost:13313",
|
||||
"browserAttachedRequired": true,
|
||||
"hostBridgeRequired": true
|
||||
},
|
||||
"stageEvidence": [
|
||||
{
|
||||
"stage": "detect",
|
||||
"evidence": [
|
||||
"queryAbnorList",
|
||||
"queryHistoryEnergyCharge",
|
||||
"getAllSubMgtOrgTreeByOrgCode",
|
||||
"MonitorServices/getMonitorLog",
|
||||
"marketingServices/getOtherIphones"
|
||||
]
|
||||
},
|
||||
{
|
||||
"stage": "decide",
|
||||
"evidence": [
|
||||
"setData pending list computation",
|
||||
"previous monitor state comparison",
|
||||
"holiday/time/phone configuration checks"
|
||||
]
|
||||
},
|
||||
{
|
||||
"stage": "preview",
|
||||
"evidence": [
|
||||
"queueObj.pendingList",
|
||||
"computed call/message/audio candidate lists"
|
||||
],
|
||||
"mvpAllowed": true
|
||||
},
|
||||
{
|
||||
"stage": "act",
|
||||
"evidence": [
|
||||
"repetCtrlSend"
|
||||
],
|
||||
"blockedByDefault": true
|
||||
},
|
||||
{
|
||||
"stage": "notify",
|
||||
"evidence": [
|
||||
"mac.sendMessages",
|
||||
"mac.callOutLogin",
|
||||
"setSendMessageLog",
|
||||
"setAudioPlayLog"
|
||||
],
|
||||
"blockedByDefault": true
|
||||
},
|
||||
{
|
||||
"stage": "log",
|
||||
"evidence": [
|
||||
"setMonitorData",
|
||||
"setMonitorLog",
|
||||
"setDisposeLog",
|
||||
"messageLogInfo",
|
||||
"iphonesLogInfo"
|
||||
]
|
||||
},
|
||||
{
|
||||
"stage": "queue_next",
|
||||
"evidence": [
|
||||
"_this.autoTask",
|
||||
"_this.processQueue",
|
||||
"mac.exeTQueue"
|
||||
],
|
||||
"blockedByDefault": true
|
||||
}
|
||||
],
|
||||
"localStorageReads": [
|
||||
{
|
||||
"key": "loginUserInfo",
|
||||
"usage": "current user org and login context"
|
||||
},
|
||||
{
|
||||
"key": "markToken",
|
||||
"usage": "business gateway Authorization token"
|
||||
},
|
||||
{
|
||||
"key": "yxClassList",
|
||||
"usage": "cached organization tree/list"
|
||||
},
|
||||
{
|
||||
"key": "zhzxFkycSendTime",
|
||||
"usage": "last fee-control send/monitor time"
|
||||
}
|
||||
],
|
||||
"localServiceDependencies": [
|
||||
{
|
||||
"url": "http://localhost:13313/MonitorServices/getMonitorLog",
|
||||
"classification": "read_state"
|
||||
},
|
||||
{
|
||||
"url": "http://localhost:13313/MonitorServices/setMonitorData",
|
||||
"classification": "write_monitor_state",
|
||||
"sideEffect": true
|
||||
},
|
||||
{
|
||||
"url": "http://localhost:13313/MonitorServices/setMonitorLog",
|
||||
"classification": "write_monitor_log",
|
||||
"sideEffect": true
|
||||
},
|
||||
{
|
||||
"url": "http://localhost:13313/MonitorServices/setDisposeLog",
|
||||
"classification": "write_dispose_log",
|
||||
"sideEffect": true
|
||||
},
|
||||
{
|
||||
"url": "http://localhost:13313/MonitorServices/setAudioPlayLog",
|
||||
"classification": "write_notification_log",
|
||||
"sideEffect": true
|
||||
},
|
||||
{
|
||||
"url": "http://localhost:13313/MonitorServices/setSendMessageLog",
|
||||
"classification": "write_notification_log",
|
||||
"sideEffect": true
|
||||
},
|
||||
{
|
||||
"url": "http://localhost:13313/marketingServices/getOtherIphones",
|
||||
"classification": "configuration_read"
|
||||
},
|
||||
{
|
||||
"url": "http://localhost:13313/marketingServices/messageLogInfo",
|
||||
"classification": "write_notification_log",
|
||||
"sideEffect": true
|
||||
},
|
||||
{
|
||||
"url": "http://localhost:13313/marketingServices/iphonesLogInfo",
|
||||
"classification": "write_notification_log",
|
||||
"sideEffect": true
|
||||
}
|
||||
],
|
||||
"businessApiDependencies": [
|
||||
{
|
||||
"url": "http://yxgateway.gs.sgcc.com.cn/emss-cmc-authdata-subdomain/member/mgtOrg/getAllSubMgtOrgTreeByOrgCode",
|
||||
"classification": "read_org_tree"
|
||||
},
|
||||
{
|
||||
"url": "http://yxgateway.gs.sgcc.com.cn/emss-chargacctgf-paysrv-front/member/acctabnor/queryAbnorList",
|
||||
"classification": "read_exception_orders"
|
||||
},
|
||||
{
|
||||
"url": "http://yxgateway.gs.sgcc.com.cn/emss-custmgtf-custview-front//member/electrivity/queryHistoryEnergyCharge",
|
||||
"classification": "read_charge_history"
|
||||
},
|
||||
{
|
||||
"url": "http://yxgateway.gs.sgcc.com.cn/emss-chargacctgf-paysrv-front/member/acctabnor/repetCtrlSend",
|
||||
"classification": "dispatch_exception_order",
|
||||
"sideEffect": true,
|
||||
"blockedByDefault": true
|
||||
}
|
||||
],
|
||||
"sideEffectEvidence": [
|
||||
{
|
||||
"action": "dispatch_fee_control_exception_order",
|
||||
"signals": [
|
||||
"repetCtrlSend"
|
||||
],
|
||||
"blockedByDefault": true,
|
||||
"requiredFutureGate": "explicit_confirm_dispatch"
|
||||
},
|
||||
{
|
||||
"action": "send_sms",
|
||||
"signals": [
|
||||
"mac.sendMessages"
|
||||
],
|
||||
"blockedByDefault": true,
|
||||
"requiredFutureGate": "explicit_confirm_notify"
|
||||
},
|
||||
{
|
||||
"action": "call_phone",
|
||||
"signals": [
|
||||
"mac.callOutLogin"
|
||||
],
|
||||
"blockedByDefault": true,
|
||||
"requiredFutureGate": "explicit_confirm_notify"
|
||||
},
|
||||
{
|
||||
"action": "play_audio",
|
||||
"signals": [
|
||||
"mac.audioPlay or audio log path"
|
||||
],
|
||||
"blockedByDefault": true,
|
||||
"requiredFutureGate": "explicit_confirm_notify"
|
||||
},
|
||||
{
|
||||
"action": "write_monitor_state",
|
||||
"signals": [
|
||||
"setMonitorData",
|
||||
"setMonitorLog"
|
||||
],
|
||||
"blockedByDefault": true,
|
||||
"requiredFutureGate": "dry_run_or_explicit_monitor_write"
|
||||
},
|
||||
{
|
||||
"action": "write_dispose_log",
|
||||
"signals": [
|
||||
"setDisposeLog"
|
||||
],
|
||||
"blockedByDefault": true,
|
||||
"requiredFutureGate": "explicit_confirm_dispatch"
|
||||
},
|
||||
{
|
||||
"action": "continue_queue",
|
||||
"signals": [
|
||||
"_this.autoTask",
|
||||
"_this.processQueue",
|
||||
"mac.exeTQueue"
|
||||
],
|
||||
"blockedByDefault": true,
|
||||
"requiredFutureGate": "explicit_confirm_queue_next"
|
||||
}
|
||||
],
|
||||
"stateDependencies": [
|
||||
{
|
||||
"name": "previous_monitor_log",
|
||||
"source": "MonitorServices/getMonitorLog"
|
||||
},
|
||||
{
|
||||
"name": "current_user_org",
|
||||
"source": "localStorage.loginUserInfo"
|
||||
},
|
||||
{
|
||||
"name": "gateway_token",
|
||||
"source": "localStorage.markToken"
|
||||
},
|
||||
{
|
||||
"name": "cached_org_tree",
|
||||
"source": "localStorage.yxClassList"
|
||||
},
|
||||
{
|
||||
"name": "last_fee_control_send_time",
|
||||
"source": "localStorage.zhzxFkycSendTime"
|
||||
},
|
||||
{
|
||||
"name": "phone_and_holiday_configuration",
|
||||
"source": "marketingServices/getOtherIphones"
|
||||
}
|
||||
],
|
||||
"queueDependencies": [
|
||||
{
|
||||
"name": "pendingList",
|
||||
"source": "queueObj.pendingList / obj.pendingList"
|
||||
},
|
||||
{
|
||||
"name": "autoTask",
|
||||
"source": "_this.autoTask",
|
||||
"blockedByDefault": true
|
||||
},
|
||||
{
|
||||
"name": "processQueue",
|
||||
"source": "_this.processQueue",
|
||||
"blockedByDefault": true
|
||||
},
|
||||
{
|
||||
"name": "exeTQueue",
|
||||
"source": "mac.exeTQueue",
|
||||
"blockedByDefault": true
|
||||
}
|
||||
],
|
||||
"detectPreviewCandidate": {
|
||||
"defaultMode": "detect_preview",
|
||||
"allowedOutputs": [
|
||||
"pendingList",
|
||||
"summary",
|
||||
"actionPlan",
|
||||
"blockedSideEffects"
|
||||
],
|
||||
"allowedReads": [
|
||||
"localStorage reads",
|
||||
"business query APIs",
|
||||
"localhost read/config APIs"
|
||||
],
|
||||
"blockedWrites": [
|
||||
"dispatch",
|
||||
"SMS",
|
||||
"phone call",
|
||||
"audio play",
|
||||
"monitor/log writes unless dry-run isolated",
|
||||
"queue continuation"
|
||||
]
|
||||
},
|
||||
"blockedByDefault": [
|
||||
"repetCtrlSend",
|
||||
"mac.sendMessages",
|
||||
"mac.callOutLogin",
|
||||
"mac.audioPlay",
|
||||
"_this.autoTask",
|
||||
"_this.processQueue",
|
||||
"mac.exeTQueue",
|
||||
"setDisposeLog",
|
||||
"setMonitorData",
|
||||
"setMonitorLog",
|
||||
"setSendMessageLog",
|
||||
"setAudioPlayLog"
|
||||
],
|
||||
"nextRecommendedRoute": "monitoring-action-ir-contract"
|
||||
}
|
||||
36
tests/fixtures/generated_scene/multi_mode/index.html
vendored
Normal file
36
tests/fixtures/generated_scene/multi_mode/index.html
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Multi Mode Fixture</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const sourceUrl = "http://20.76.57.61:18080/gsllys";
|
||||
|
||||
function queryReport(period_mode, orgno) {
|
||||
if (period_mode === "month") {
|
||||
return $.ajax({
|
||||
url: "http://20.76.57.61:18080/gsllys/monthReport",
|
||||
type: "POST",
|
||||
contentType: "application/x-www-form-urlencoded",
|
||||
data: { orgno, rows: 1000, page: 1, tjzq: "month" }
|
||||
});
|
||||
}
|
||||
if (period_mode === "week") {
|
||||
return $.ajax({
|
||||
url: "http://20.76.57.61:18080/gsllys/weekReport",
|
||||
type: "POST",
|
||||
contentType: "application/x-www-form-urlencoded",
|
||||
data: { orgno, rows: 1000, page: 1, tjzq: "week" }
|
||||
});
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function renderTable(response) {
|
||||
return response.content || [];
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user