feat: layout import steps from extracted-content flags
**PR:**feat: layout import steps from extracted-content flags (#4651)
source_automation: “voyager-merged-pr” run_date: “2026-07-12” anchor_pr_number: 4651 pr_number: 4651 pr_title: “feat: layout import steps from extracted-content flags” pr_url: “https://github.com/adastralab-ai/voyager/pull/4651 ↗” author: “Yuxing Wang / ongyuxing” merged_at: “2026-07-11T15:03:28Z” modules: [“idl/typespec/services/task.tsp”,“backend/go/internal/brand/task_api.go”,“packages/site/src/app/(main)/brands/new”,“packages/site/src/gen/api”,“packages/admin/src/gen/api”,“backend/workers/agent/src/gen/api”] files_changed: 12 learning_tags: [“brand-import”,“api-contract”,“task-result-shape”,“frontend-state-consistency”,“codegen-boundary”,“progress-ui”] business_line: null related_prs: [4309,4389,4580] line_stage: null open_questions: [] feishu_doc_url: “https://my.feishu.cn/docx/Mnbwdz1SYoJKrixMgN9cooCznth ↗”#
正文来源:飞书学习文档 ↗。以下为通过个人 Feishu API 获取并转换后的完整 Markdown 正文。
今日选择#
**PR:**feat: layout import steps from extracted-content flags (#4651)
**作者:**Yuxing Wang / ongyuxing
**Merge 时间:**2026-07-11T15:03:28Z(Australia/Melbourne 2026-07-12 01:03:28)
链接:https://github.com/adastralab-ai/voyager/pull/4651 ↗
**模块:**idl/typespec/services/task.tsp;backend/go/internal/brand/task_api.go;packages/site/src/app/(main)/brands/new;生成 API types/zod。
**学习标签:**brand-import、api-contract、task-result-shape、frontend-state-consistency、codegen-boundary、progress-ui。
为什么值得学#
- 它把“进度动画应该展示哪些步骤”从前端猜测改成后端任务结果中的显式能力信号,避免 UI 在 finalize 前后使用两套不一致的真相来源。
- 它没有把完整 draft 内容提前暴露给前端,只加 hasFonts、hasColors、hasGuidelines、hasImages 四个布尔位,保持任务结果的契约很窄。
- 它按 Voyager 的 contract-first 路径修改 TypeSpec,再同步生成 Go/TS/zod 类型,让跨 Go 后端、site、admin、agent worker 的调用面一致。
- 它保留 finalize 后从真实 BrandDetails 计算可见步骤的逻辑,因此初始阶段用 draft presence,最终阶段用持久化 brand 内容,两个阶段各自用最可靠的数据源。
关键代码#
1. TypeSpec 把 UI 需要的 presence 变成任务结果契约#
// A completed brand_import has no brand yet. The brand is created on finalize.
// Logos are the only draft content surfaced for review. The `has*` flags let
// the review UI lay out its steps before the brand exists. The section
// contents arrive with the finalized brand.
model BrandImportTaskResult {
logos: BrandImportDraftLogo[];
hasFonts: boolean;
hasColors: boolean;
hasGuidelines: boolean;
hasImages: boolean;plaintext设计点:这里明确区分“draft review 阶段”和“finalized brand 阶段”。前端只需要知道某类内容是否存在,不需要拿到 guidelines/images/texts 的完整 draft,因此契约保持最小。
2. Go 转换层在已有 draft 上派生 flags#
hasColors := false
for _, p := range draft.Palettes {
if len(p.Colors) > 0 {
hasColors = true
break
}
}
...
Result: api.BrandImportTaskResult{
Logos: logos,
HasFonts: len(draft.Texts) > 0,
HasColors: hasColors,
HasGuidelines: len(draft.Guidelines) > 0,
HasImages: len(draft.Images) > 0,
},plaintext设计点:presence 判断放在后端任务 API 的 projection 层,而不是前端或 finalize 接口里临时拼。颜色还特意检查 palette 内是否真的有 colors,避免仅有空 palette 就误报。
3. 前端把初始步骤映射成同一组 processSteps 的过滤器#
export const getInitialSteps = (result: BrandImportTaskResult) => {
const present: Record<ProcessStepKey, boolean> = {
fonts: result.hasFonts,
colors: result.hasColors,
guidelines: result.hasGuidelines,
assets: result.hasImages,
};
return processSteps.filter((step) => present[step.key]);
};plaintext设计点:初始阶段没有复制一套步骤定义,只是给现有 processSteps 增加一个基于 task result 的过滤入口。这样文案、图标、渲染组件仍然集中在 ProcessStep 的同一套定义里。
4. 进度组件按阶段切换数据源#
const [logos, setLogos] = useState(draft.logos);
...
const visibleSteps =
finalizeData == null
? getInitialSteps(draft)
: getVisibleSteps(finalizeData.brand);plaintext设计点:finalize 前根据 draft flags 布局,finalize 后根据真实 brand 内容布局。这个切换让动画阶段不会展示最终一定为空的步骤,也不会在 brand 创建后突然消失。
和最近学习记录的关系#
和最近几天 agent runtime / image generation / deck eval 的主线没有直接模块关系;那些 PR 主要在 agent 协议、工具结果和用户确认边界上收敛。
但工程主题有相似性:#4309、#4389、#4580 都强调“跨层选择不能停留在 UI 状态”,需要进入 API/schema/runtime 契约;#4651 把同一原则用在品牌导入流程上,把“哪些步骤会有内容”变成后端 task result 的显式 contract。
我会怎么吸收#
- 当 UI 的中间态依赖异步任务产物时,优先让任务结果返回窄 presence/summary 信号,而不是让前端猜或提前暴露完整内部 draft。
- 阶段性数据源要明确:finalize 前用任务 draft projection,finalize 后用持久化实体;不要用一个阶段的数据假装覆盖所有阶段。
- 跨语言仓库里,新增字段要从 schema 源头进入,生成物只是验证契约传播,不在生成文件里手写补洞。
边界/风险#
- 明显风险不大,变更面小且 CI 中 IDL、Go、Frontend Quality、Frontend Unit Test、Frontend Browser Test 都成功。
- 一个测试缺口是 PR 没有新增针对 getInitialSteps 或 BuildAPITask flags 的单元测试;如果后续 draft 结构变化,这组 presence 规则可能被无意破坏。
- hasFonts 使用 len(draft.Texts) > 0 作为 fonts presence 的代理,语义依赖现有 brand import draft 结构;如果之后 texts 里混入非字体相关样本,需要重新审视字段命名或派生规则。
候选说明#
按 Australia/Melbourne 日期,2026-07-12 有 4 个已 merge 到 main 的候选:#4653、#4651、#4627、#4609。昨天窗口还有 12 个候选,其中 #4591 已在学习日志中记录。
最终选择 #4651,因为它用很小的 diff 解决了跨异步任务、API 契约和前端进度 UI 一致性的问题;相比今天几个更偏视觉还原或局部交互修复的 PR,它的工程设计信号更集中。