Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# DOUBAO-VIDEO-PAGE-READINESS-01 验收收口单

- 日期:2026-09-10
- 裁决:PASS
- supersedes:`2026-09-10-doubao-video-page-readiness-01.md` 中“尚未人工验收”的状态
- 基线:`origin/main@dfbb7f2028bd88caf838c52f5785e3130e594e7a`

## 治理与冻结范围

已按总架构师治理规则复核。冻结范围仍为九阶段视频页就绪、有界稳定采样与脱敏诊断、提交前最多一次页面恢复及提交不确定禁止重发;未扩入调度、下载、额度或 Dola 验证。

## 验收证据

- 单包专项:3 files / 189 tests PASS。
- 单包全量:48 files / 1012 tests PASS;TypeScript、工程边界、Renderer/Main build、pack PASS;ESLint 0 error / 147 warning。
- 与第三包联合候选:49 files / 1030 tests PASS;TypeScript、lint、build、pack、`git diff --check` 全部 PASS。
- 真实平台:授权范围内完成一次普通 5 秒无素材视频,仅提交一次;页面就绪链未发生重复点击或不确定重发,任务最终绑定真实产物。
- 重启后保持同一具体会话;没有跳转到 `/chat/` 根页或再次提交。

证据目录:`D:\豆包工作室\_acceptance\p2-p3-20260910-204549`。其中 `g1-p2-*`、`g3-integration-*`、`g6-result.json`、`final-acceptance.json` 为本裁决依据。

## 五态声明

- 已实现:是。
- CI:尚未运行远端 CI。
- 人工/真实验收:PASS(授权范围内单次真实平台链)。
- 已合并:否。
- 已部署/已发布:否。

Dola 仍未验证;未把真实账号数据、Session、任务台账或验收副本纳入仓库。
74 changes: 74 additions & 0 deletions docs/handoffs/2026-09-10-doubao-video-page-readiness-01.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# DOUBAO-VIDEO-PAGE-READINESS-01 实施交接单

- 日期:2026-09-10
- 状态:代码已实现、自动化测试通过;未人工验收、未执行真实任务、未提交、未推送、未部署
- 基线:`origin/main@dfbb7f2028bd88caf838c52f5785e3130e594e7a`
- 分支:`codex/doubao-video-page-readiness-01`
- 工作树:`D:\豆包工作室\doubao-video-page-readiness-01`

## 治理与冻结验收包

开工前已读取总架构师治理规则,包括中央 `memory/INDEX.md`、`DEVELOPMENT_EFFICIENCY_GOVERNANCE.md`、`ACTIVE_DEVELOPMENT_DIRECTION.md`、`CURRENT.md`、`QUEUE.md`,以及豆包仓库 `AGENTS.md`、`ROADMAP.md`、相关源码/测试和 `2026-09-08-doubao-studio-236-t01-t02-production-usage-report-r1.md`。

本包冻结为一个核心目标、三个 P0:

1. 九阶段视频页就绪链:账号、新对话、视频入口、模型、比例、时长、素材、提示词、发送控件。
2. 每阶段有界等待、连续稳定采样和脱敏诊断;禁止保存页面正文、提示词、素材绝对路径、Cookie、Token 或 Session。
3. 提交前页面变化最多恢复一次;提交不确定禁止自动重发,只有明确平台回执才进入生成中。

非目标:执行意图、依赖策略、多账号观察流水线、local-control、下载归档、真实素材上传和真实额度消费。Dola 未做真实验证,不宣称可用。

## 实现结果

- 新增统一 `VideoPageReadinessStage` 九阶段模型和版本化诊断 schema。
- 将旧 `model + composite` 探针拆为 `model + aspectRatio + duration`,连续三次稳定才通过。
- 页面结构指纹仅编码适配器版本、页面变体与三个能力位;不读取或持久化网页正文。
- `BrowserPanel` 在九个实际执行节点更新任务快照,重启后可回读当前阶段、耗时、尝试数、稳定样本、恢复次数、缺失控件和结构版本。
- 模型、比例、时长、素材、提示词和发送控件失败使用独立机器码,不再退化为笼统 `page_changed`。
- 参数配置发生已知就绪错误时,仅在提交意图产生前重新识别一次;第二次失败立即停止。
- 沿用原有不可重复副作用保护:先持久化提交意图,点击后无明确回执进入 `submission_uncertain`,不自动重发;明确回读后才进入 `generating`。
- 旧运行快照阶段与错误码继续可读,持久化归一化对新增字段执行白名单和长度限制。

## 精确文件清单

Tracked 修改(9):

1. `main/utils/persistenceNormalization.ts`
2. `packages/contracts/src/domain.ts`
3. `packages/contracts/src/enums.ts`
4. `src/components/BrowserPanel.tsx`
5. `src/utils/doubaoBridge.ts`
6. `src/utils/videoControlReadiness.ts`
7. `tests/unit/generationControlReadiness.test.ts`
8. `tests/unit/persistenceNormalization.test.ts`
9. `tests/unit/videoCapability.test.ts`

Untracked 新增(1):

1. `docs/handoffs/2026-09-10-doubao-video-page-readiness-01.md`

Tracked 删除:无。

## 门禁证据

- 专项:5 files / 255 pass / 0 fail。
- TypeScript:contracts、main、renderer、test 四套配置通过。
- 修改文件 ESLint:0 error / 34 warning;warning 为既有复杂度、React hook、any 等告警。
- 全量 `pnpm run validate`:PASS(exit code 0)。
- 全量 ESLint:0 error / 147 warning,未超过 149 上限。
- 工程/Contracts 边界:PASS。
- 全量测试:48 files / 1012 pass / 0 fail。
- Renderer/Main 构建:PASS。
- `git diff --check`:PASS。

## 状态与停止声明

- 代码已实现:是。
- CI:未运行。
- 人工验收:未执行。
- 真实任务执行:未执行;未上传素材、未点击平台提交、未消耗额度。
- Git:全部改动未暂存、未提交、未推送,未创建或修改 PR。
- 部署/发布:未部署、未启动或重启豆包工作室、未发布。
- 受保护资产:未修改 Cookie、Token、账号 Session、任务台账、视频产物或真实平台任务。

下一步应先由总架构师独立复验本包,再按逐项授权处理候选提交/PR;前三包全部自动化收口后,才使用一次普通 5 秒视频做真实验收。
11 changes: 10 additions & 1 deletion main/utils/persistenceNormalization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -441,13 +441,22 @@ function normalizeRuntime(raw: unknown, taskMode: GenerationMode, now: string):
submittedAt: (() => { const v = r.submittedAt; return isValidISODate(v) ? v : undefined; })(),
conversationUrl: asNonEmptyString(r.conversationUrl) ?? undefined,
controlReadiness: isObject(r.controlReadiness) ? {
schemaVersion: r.controlReadiness.schemaVersion === 1 ? 1 : undefined,
ready: typeof r.controlReadiness.ready === 'boolean' ? r.controlReadiness.ready : undefined,
currentStage: optionalOneOf(r.controlReadiness.currentStage, ['account', 'new_conversation', 'video_entry', 'model', 'aspect_ratio', 'duration', 'assets', 'prompt', 'submission_controls']) as NonNullable<TaskRunSnapshot['controlReadiness']>['currentStage'],
modeEntryElapsedMs: asNonNegativeNumber(r.controlReadiness.modeEntryElapsedMs, 0) || undefined,
attempts: asNonNegativeNumber(r.controlReadiness.attempts, 0),
elapsedMs: asNonNegativeNumber(r.controlReadiness.elapsedMs, 0),
stableSamples: asNonNegativeNumber(r.controlReadiness.stableSamples, 0) || undefined,
recoveryAttempts: asNonNegativeNumber(r.controlReadiness.recoveryAttempts, 0) || undefined,
pageStructureVersion: asNonEmptyString(r.controlReadiness.pageStructureVersion)?.slice(0, 96),
missingControl: optionalOneOf(r.controlReadiness.missingControl, ['account_page', 'conversation_editor', 'video_entry', 'model_control', 'aspect_ratio_control', 'duration_control', 'asset_upload', 'prompt_editor', 'send_control']) as NonNullable<TaskRunSnapshot['controlReadiness']>['missingControl'],
modelVisibleAtMs: asNonNegativeNumber(r.controlReadiness.modelVisibleAtMs, 0) || undefined,
aspectRatioVisibleAtMs: asNonNegativeNumber(r.controlReadiness.aspectRatioVisibleAtMs, 0) || undefined,
durationVisibleAtMs: asNonNegativeNumber(r.controlReadiness.durationVisibleAtMs, 0) || undefined,
compositeVisibleAtMs: asNonNegativeNumber(r.controlReadiness.compositeVisibleAtMs, 0) || undefined,
stableAtMs: asNonNegativeNumber(r.controlReadiness.stableAtMs, 0) || undefined,
failureStage: optionalOneOf(r.controlReadiness.failureStage, ['mode_entry', 'model_control', 'composite_control', 'stable_readback', 'final_readback']) as NonNullable<TaskRunSnapshot['controlReadiness']>['failureStage'],
failureStage: optionalOneOf(r.controlReadiness.failureStage, ['account', 'new_conversation', 'video_entry', 'model', 'aspect_ratio', 'duration', 'assets', 'prompt', 'submission_controls', 'mode_entry', 'model_control', 'composite_control', 'stable_readback', 'final_readback']) as NonNullable<TaskRunSnapshot['controlReadiness']>['failureStage'],
} : undefined,
generationConfirmation: isObject(r.generationConfirmation) &&
isValidISODate(r.generationConfirmation.detectedAt) &&
Expand Down
11 changes: 10 additions & 1 deletion packages/contracts/src/domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,22 @@ export interface TaskRunSnapshot {
submittedAt?: string;
conversationUrl?: string;
controlReadiness?: {
schemaVersion?: 1;
ready?: boolean;
currentStage?: 'account' | 'new_conversation' | 'video_entry' | 'model' | 'aspect_ratio' | 'duration' | 'assets' | 'prompt' | 'submission_controls';
modeEntryElapsedMs?: number;
attempts: number;
elapsedMs: number;
stableSamples?: number;
recoveryAttempts?: number;
pageStructureVersion?: string;
missingControl?: 'account_page' | 'conversation_editor' | 'video_entry' | 'model_control' | 'aspect_ratio_control' | 'duration_control' | 'asset_upload' | 'prompt_editor' | 'send_control';
modelVisibleAtMs?: number;
aspectRatioVisibleAtMs?: number;
durationVisibleAtMs?: number;
compositeVisibleAtMs?: number;
stableAtMs?: number;
failureStage?: 'mode_entry' | 'model_control' | 'composite_control' | 'stable_readback' | 'final_readback';
failureStage?: 'account' | 'new_conversation' | 'video_entry' | 'model' | 'aspect_ratio' | 'duration' | 'assets' | 'prompt' | 'submission_controls' | 'mode_entry' | 'model_control' | 'composite_control' | 'stable_readback' | 'final_readback';
};
generationConfirmation?: {
detectedAt: string;
Expand Down
7 changes: 7 additions & 0 deletions packages/contracts/src/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,14 @@ export type TaskErrorCode =
| 'generation_confirmation_required'
| 'manual_review_required'
| 'video_mode_entry_not_ready'
| 'video_account_not_ready'
| 'video_new_conversation_not_ready'
| 'video_model_control_not_ready'
| 'video_aspect_ratio_control_not_ready'
| 'video_duration_control_not_ready'
| 'video_assets_not_ready'
| 'video_prompt_not_ready'
| 'video_submission_controls_not_ready'
| 'video_composite_control_not_ready'
| 'video_stable_readback_not_ready'
| 'video_final_readback_not_ready'
Expand Down
Loading
Loading