收敛 design_doc 未使用接口并补齐切分 sidecar 收尾
- run-workflow 干净跑完后调用 finalize_separation 清理 sidecar,有回填问题时保留给编辑器恢复,与前端切分链路一致 - 检查点恢复校验轮次归属文档,漂移与写回冲突说明带上基线 revision - WorkflowLog 删除仅供测试的 path 访问器,测试直接用 workflow_log_path - mod.rs 不再导出仅内部使用的 next_ui_design_path,消除未使用告警 - 技术方案同步 sidecar 收尾口径
This commit is contained in:
@@ -85,10 +85,6 @@ impl WorkflowLog {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn path(&self) -> &Path {
|
||||
&self.path
|
||||
}
|
||||
|
||||
/// 读取最后一轮。文件不存在或没有完整 `run` 行时返回 `None`。
|
||||
pub(crate) fn last_round(&self) -> Result<Option<WorkflowRound>, String> {
|
||||
let Some(content) = read_log_content(&self.path)? else {
|
||||
@@ -274,6 +270,10 @@ mod tests {
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
|
||||
fn log_path(directory: &tempfile::TempDir) -> PathBuf {
|
||||
workflow_log_path(directory.path(), "ui/UI 设计 1.json").expect("workflow log path")
|
||||
}
|
||||
|
||||
fn log(directory: &tempfile::TempDir) -> WorkflowLog {
|
||||
WorkflowLog::open(directory.path(), "ui/UI 设计 1.json").expect("open workflow log")
|
||||
}
|
||||
@@ -368,7 +368,7 @@ mod tests {
|
||||
{
|
||||
let mut file = OpenOptions::new()
|
||||
.append(true)
|
||||
.open(log.path())
|
||||
.open(log_path(&directory))
|
||||
.expect("open raw log");
|
||||
file.write_all(b"{\"type\":\"separate\",\"at\":12,\"dt")
|
||||
.expect("write torn tail");
|
||||
@@ -383,7 +383,7 @@ mod tests {
|
||||
.expect("append after torn tail");
|
||||
let round = log.last_round().expect("scan").expect("round");
|
||||
assert_eq!(round.outcome, RoundOutcome::WrittenBack { revision: 2 });
|
||||
let content = std::fs::read_to_string(log.path()).expect("read log");
|
||||
let content = std::fs::read_to_string(log_path(&directory)).expect("read log");
|
||||
assert!(!content.contains("\"dt"));
|
||||
assert!(content.ends_with('\n'));
|
||||
}
|
||||
@@ -392,8 +392,9 @@ mod tests {
|
||||
fn malformed_complete_line_fails_closed() {
|
||||
let directory = tempfile::tempdir().expect("temp dir");
|
||||
let log = log(&directory);
|
||||
std::fs::create_dir_all(log.path().parent().expect("parent")).expect("create dir");
|
||||
std::fs::write(log.path(), "{\"type\":\"nonsense\"}\n").expect("write bad line");
|
||||
std::fs::create_dir_all(log_path(&directory).parent().expect("parent"))
|
||||
.expect("create dir");
|
||||
std::fs::write(log_path(&directory), "{\"type\":\"nonsense\"}\n").expect("write bad line");
|
||||
assert!(log.last_round().is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ mod steps;
|
||||
mod test_support;
|
||||
|
||||
pub(crate) use creation::{
|
||||
create_ui_design_doc_from_images, next_ui_design_path, CreateUiDesignDocFromImagesInput,
|
||||
UiDesignDocCreated, UiDesignImageReference,
|
||||
create_ui_design_doc_from_images, CreateUiDesignDocFromImagesInput, UiDesignDocCreated,
|
||||
UiDesignImageReference,
|
||||
};
|
||||
pub(crate) use run_workflow::{run_ui_design_doc_workflow, RunUiDesignDocWorkflowInput};
|
||||
|
||||
@@ -13,6 +13,7 @@ use super::cut_images::register_cut_image_sprites;
|
||||
use super::steps::recognize::apply_recognition;
|
||||
use super::steps::separate::{add_sprite_assets, apply_separation};
|
||||
use crate::enforce_project_permission_policy;
|
||||
use crate::ui_editor::commands::separation::finalize_separation;
|
||||
use crate::ui_editor::commands::{
|
||||
recognize_ui_impl_with_provider, separate_ui_impl, RecognitionDTO, SeparationDTO,
|
||||
};
|
||||
@@ -73,7 +74,7 @@ pub(crate) async fn run_ui_design_doc_workflow(
|
||||
.filter(|round| round.outcome == RoundOutcome::Open);
|
||||
let recovered_from_checkpoint = resume.is_some();
|
||||
let mut progress = match &resume {
|
||||
Some(round) => resume_progress(root, &log, &snapshot, round)?,
|
||||
Some(round) => resume_progress(root, &log, &snapshot, round, &input.asset_id)?,
|
||||
None => {
|
||||
let state = snapshot.state.clone();
|
||||
log.append(&CheckpointLine::Run {
|
||||
@@ -145,8 +146,13 @@ pub(crate) async fn run_ui_design_doc_workflow(
|
||||
})?;
|
||||
snapshot.revision
|
||||
} else {
|
||||
write_back(root, &input, &log, snapshot.revision, &progress.state)?
|
||||
write_back(&input, &log, snapshot.revision, &progress.state)?
|
||||
};
|
||||
// 与前端切分链路一致:干净跑完才清理 sidecar;有回填问题或问题节点时保留,
|
||||
// 交给编辑器显示恢复入口。切分 op 内部的细粒度进度仍由 SeparationState 承担。
|
||||
if progress.backfill_errors.is_empty() && separate_dto.problematic_nodes.is_empty() {
|
||||
finalize_separation(root, &input.asset_id)?;
|
||||
}
|
||||
Ok(RunUiDesignDocWorkflowOutput {
|
||||
asset_id: input.asset_id,
|
||||
relative_path,
|
||||
@@ -167,7 +173,14 @@ fn resume_progress(
|
||||
log: &WorkflowLog,
|
||||
snapshot: &UiDesignStateSnapshot,
|
||||
round: &WorkflowRound,
|
||||
asset_id: &str,
|
||||
) -> Result<WorkflowProgress, String> {
|
||||
if round.document_asset_id != asset_id {
|
||||
return Err(format!(
|
||||
"工作流检查点属于其它 UI 设计文档({}),拒绝按本轮恢复",
|
||||
round.document_asset_id
|
||||
));
|
||||
}
|
||||
let state: State = serde_json::from_value(round.base_state.clone())
|
||||
.map_err(|error| format!("工作流检查点的起始 State 无法还原:{error}"))?;
|
||||
let recorded_recognize = round
|
||||
@@ -200,10 +213,7 @@ fn resume_progress(
|
||||
at: checkpoint_timestamp(),
|
||||
reason: DRIFT_REASON.to_string(),
|
||||
})?;
|
||||
return Err(
|
||||
"UI 设计文档在本次工作流中途被改动,本轮已作废;请重新调用 run-workflow 开新一轮"
|
||||
.to_string(),
|
||||
);
|
||||
return Err(drift_message(snapshot.revision, round));
|
||||
}
|
||||
Ok(WorkflowProgress {
|
||||
state,
|
||||
@@ -233,7 +243,6 @@ fn rebuild_target_state(
|
||||
}
|
||||
|
||||
fn write_back(
|
||||
root: &Path,
|
||||
input: &RunUiDesignDocWorkflowInput,
|
||||
log: &WorkflowLog,
|
||||
expected_revision: u64,
|
||||
@@ -254,19 +263,27 @@ fn write_back(
|
||||
})?;
|
||||
Ok(revision)
|
||||
}
|
||||
SaveUiDesignStateResult::Conflict { .. } => {
|
||||
SaveUiDesignStateResult::Conflict { current } => {
|
||||
log.append(&CheckpointLine::Outdated {
|
||||
at: checkpoint_timestamp(),
|
||||
reason: DRIFT_REASON.to_string(),
|
||||
})?;
|
||||
Err(
|
||||
"UI 设计文档在本次工作流中途被改动,本轮已作废;请重新调用 run-workflow 开新一轮"
|
||||
.to_string(),
|
||||
)
|
||||
Err(format!(
|
||||
"UI 设计文档在本次工作流中途被改动(写回期望 revision {expected_revision},当前 {}),本轮已作废;请重新调用 run-workflow 开新一轮",
|
||||
current.revision
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 漂移说明带上检查点轮次的基线,便于判断是外部保存还是本轮重放。
|
||||
fn drift_message(current_revision: u64, round: &WorkflowRound) -> String {
|
||||
format!(
|
||||
"UI 设计文档在本次工作流中途被改动(检查点第 {} 轮基线 revision {},当前 revision {current_revision}),本轮已作废;请重新调用 run-workflow 开新一轮",
|
||||
round.at, round.base_revision
|
||||
)
|
||||
}
|
||||
|
||||
fn to_checkpoint_value<T: Serialize>(value: &T) -> Result<Value, String> {
|
||||
serde_json::to_value(value).map_err(|error| format!("序列化工作流检查点行失败:{error}"))
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
| --- | --- | --- |
|
||||
| `recognize` | `recognize_ui_impl_with_provider` | `ui_trees`(每棵树的 `src_ui_design` 指向文档内设计图) |
|
||||
| `separate` | `separate_ui_impl` | 切分图落盘 → 登记 asset → 转 `SpriteAsset` → 写入 State → 回填 `target_graphic`、清 `component_status`、写 `NeedReview` |
|
||||
| `write-back` | `save_ui_design_state_at` | 文档新 `revision` |
|
||||
| `write-back` | `save_ui_design_state_at` | 文档新 `revision`;没有回填问题且没有问题节点时再 `finalize_separation` 清理 sidecar |
|
||||
|
||||
不再有合并、组件绑定与页面级 profile/finalize 阶段;`recognize` 之前不做任何前置发现。
|
||||
|
||||
@@ -55,6 +55,8 @@
|
||||
- 漂移:文档在轮次中途被改动(当前 State 与 `run` 行快照不一致)时,追加一行 `outdated` 并**返回错误**,不在同一次调用里自动重开新一轮;下一次调用看到 `outdated` 才从头开新一轮,且以当前文档为基准。
|
||||
- 写回幂等:`save` 成功但 `write-back` 行没追加时,重放会重建出同一份目标 State,此时按「目标 State 与文档当前 State 相等」判定为已写完,直接补 `write-back` 行并返回成功。这条判据成立的前提是工作流这条路不产生随机身份:识别树的根节点 id 来自 DTO,切图资源 id 走 manifest 的 by-path 复用。
|
||||
- 切图资源不回滚:切分产出的图片与已登记资源在失败后保留,重放靠 by-path 复用接上,不做回滚清理。
|
||||
- 写回成功后才清理 sidecar:与前端切分链路一致,`backfill_errors` 为空且 `problematic_nodes` 为空时调用
|
||||
`finalize_separation`;有回填问题或问题节点时保留 sidecar,交给编辑器显示恢复入口。
|
||||
- 切分 op 内部的细粒度恢复仍由 `SeparationState` 承担(`ui-editor-separation-state.v2` sidecar),日志只记录工作流层面的步骤完成,不复制它的进度。
|
||||
- 不引入跨语言 fixture 比对:镜像的四个 seam 都是简单变换,靠同语义实现与各自单测覆盖,不为它们额外维护一套 golden。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user