Merge remote-tracking branch 'web/master' into feat/five_min_design

冲突两处,均为两侧独立新增:
- provider_tool_plan.rs:本分支的 force_autonomous_owner_artifact_delivery 与
  master 的 force_root_goal_contract 是各自独立的 let 绑定,两者都保留。
- provider_request_builders.rs:本分支新增测试
  full_dag_pre_code_owner_requests_do_not_advertise_manual_verification,
  master 把紧随其后的 trusted_root_supervisor_receives_dynamic_goal_control_tools
  改名为 trusted_root_supervisor_first_turn_only_receives_goal_contract_tool。
  保留本分支新增的测试,共享的那个测试采用 master 的新名(其函数体已随
  master 自动合并)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-13 08:28:21 +00:00
60 changed files with 1917 additions and 401 deletions
@@ -1396,6 +1396,71 @@ describe('useImageCanvasGenerationWorkflow', () => {
).toMatchObject({ kind: 'pending', project: foreignProject });
});
it('rejects an incomplete matching task resource instead of treating it as an applied result', () => {
const operationId = 'perfect-pixel-corrupted-resource';
const project = createPerfectPixelProject(operationId, 'applied');
project.resources[0] = {
...project.resources[0]!,
objectKey: null,
};
expect(
inspectPerfectPixelProjectSnapshot(project, {
operationId,
taskId: `pixel-art-snap-${operationId}`,
}),
).toMatchObject({
kind: 'conflict',
project,
message: '完美像素任务资源记录不完整或不属于当前项目,无法自动确认结果。',
});
});
it('uses the server-confirmed stable resource id to reject a corrupted task id', () => {
const operationId = 'perfect-pixel-corrupted-task';
const project = createPerfectPixelProject(operationId, 'dialog-missing');
project.resources[0] = {
...project.resources[0]!,
taskId: 'pixel-art-snap-another-operation',
objectKey: null,
};
expect(
inspectPerfectPixelProjectSnapshot(
project,
{
operationId,
taskId: `pixel-art-snap-${operationId}`,
},
project.resources[0]!.resourceId,
),
).toMatchObject({
kind: 'conflict',
project,
message: '完美像素任务资源记录不完整或不属于当前项目,无法自动确认结果。',
});
});
it('rejects a settled dialog whose generated layer points at another resource', () => {
const operationId = 'perfect-pixel-mismatched-layer';
const project = createPerfectPixelProject(operationId, 'applied');
project.layers[0] = {
...project.layers[0]!,
resourceId: 'resource-other',
};
expect(
inspectPerfectPixelProjectSnapshot(project, {
operationId,
taskId: `pixel-art-snap-${operationId}`,
}),
).toMatchObject({
kind: 'conflict',
project,
message: '完美像素占位与任务资源的画布关联不一致,无法自动应用。',
});
});
it('opens a movable canvas generation placeholder and keeps toolbar state active', () => {
render(<GenerationWorkflowHarness />);
@@ -3265,6 +3330,81 @@ describe('useImageCanvasGenerationWorkflow', () => {
});
});
it('reconciles a retried existing-result marker against its corrupted stable resource as a conflict', async () => {
const applyProjectSnapshot = vi.fn();
let corruptedProject: EditorProjectSnapshot | undefined;
snapImageToPerfectPixelsMock
.mockImplementationOnce(async (request: EditorPixelArtSnapInput) =>
createMismatchedPerfectPixelResult(request),
)
.mockImplementationOnce(async (request: EditorPixelArtSnapInput) => {
const operationId = request.canvasCompletion.dialogId;
corruptedProject = createPerfectPixelProject(
operationId,
'dialog-missing',
);
corruptedProject.resources[0] = {
...corruptedProject.resources[0]!,
taskId: 'pixel-art-snap-corrupted-task',
objectKey: null,
};
throw new ApiClientError({
message: '同一完美像素操作已有权威结果,请先读取项目状态对账。',
status: 409,
code: 'HTTP_409',
details: {
operationResultAlreadyExists: true,
resultResourceId: corruptedProject.resources[0]!.resourceId,
},
});
});
loadEditorProjectMock.mockImplementation(async () => {
const request = snapImageToPerfectPixelsMock.mock.calls.at(-1)?.[0] as
| EditorPixelArtSnapInput
| undefined;
return snapImageToPerfectPixelsMock.mock.calls.length === 1
? createConflictingPerfectPixelProject(
request!.canvasCompletion.dialogId,
)
: corruptedProject!;
});
render(
<GenerationWorkflowHarness
projectId="project-1"
initialLayers={[
createLayer({
objectKey: 'generated-images/editor/source.png',
src: '/generated-images/editor/source.png',
}),
]}
applyProjectSnapshot={applyProjectSnapshot}
flushProjectPersistence={vi.fn(async () => {})}
/>,
);
fireEvent.click(screen.getByRole('button', { name: '完美像素' }));
await waitFor(() => {
expect(screen.getByTestId('dialog').textContent).toContain(
'pending-confirmation',
);
});
fireEvent.click(screen.getByRole('button', { name: '重试完美像素' }));
await waitFor(() => {
expect(screen.getByTestId('dialog-error').textContent).toContain(
'完美像素任务资源记录不完整或不属于当前项目,无法自动确认结果。',
);
});
expect(snapImageToPerfectPixelsMock).toHaveBeenCalledTimes(2);
expect(loadEditorProjectMock).toHaveBeenCalledTimes(2);
expect(applyProjectSnapshot).not.toHaveBeenCalled();
expect(screen.getByTestId('dialog').textContent).toContain(
'pending-confirmation',
);
});
it('anchors the first submission reconciliation window at POST time when the pre-POST flush is slow', async () => {
// 中文注释:pre-POST flush 是服务端硬前置(占位未持久化会被 409 拒收),且没有整体
// 上限。窗口若锚在 flush 之前,慢保存会让 POST 带着已过期的 reconciliation deadline
@@ -3801,6 +3941,65 @@ describe('useImageCanvasGenerationWorkflow', () => {
expect(screen.getByTestId('dialog').textContent).not.toContain('failed');
});
it('uses the server-confirmed stable resource id during existing-result reconciliation', async () => {
const applyProjectSnapshot = vi.fn();
let reconciledProject: EditorProjectSnapshot | undefined;
snapImageToPerfectPixelsMock.mockImplementationOnce(
async (request: EditorPixelArtSnapInput) => {
const operationId = request.canvasCompletion.dialogId;
reconciledProject = createPerfectPixelProject(
operationId,
'dialog-missing',
);
reconciledProject.resources[0] = {
...reconciledProject.resources[0]!,
taskId: 'pixel-art-snap-corrupted-task',
objectKey: null,
};
throw new ApiClientError({
message: '同一完美像素操作已有权威结果,请先读取项目状态对账。',
status: 409,
code: 'HTTP_409',
details: {
operationResultAlreadyExists: true,
resultResourceId: reconciledProject.resources[0]!.resourceId,
},
});
},
);
loadEditorProjectMock.mockImplementationOnce(async () => {
expect(reconciledProject).toBeDefined();
return reconciledProject!;
});
render(
<GenerationWorkflowHarness
projectId="project-1"
initialLayers={[
createLayer({
objectKey: 'generated-images/editor/source.png',
src: '/generated-images/editor/source.png',
}),
]}
applyProjectSnapshot={applyProjectSnapshot}
flushProjectPersistence={vi.fn(async () => {})}
/>,
);
fireEvent.click(screen.getByRole('button', { name: '完美像素' }));
await waitFor(() => {
expect(screen.getByTestId('dialog-error').textContent).toContain(
'完美像素任务资源记录不完整或不属于当前项目,无法自动确认结果。',
);
});
expect(loadEditorProjectMock).toHaveBeenCalledTimes(1);
expect(applyProjectSnapshot).not.toHaveBeenCalled();
expect(screen.getByTestId('dialog').textContent).toContain(
'pending-confirmation',
);
});
it('skips reconciliation for a responded failure the server did not mark', async () => {
// 中文注释:纯校验失败发生在任何 IO 之前,不可能留下对象或素材。服务端不置
// resultPersistenceStarted,客户端就不该多打两次读取,也不该附上「请核对素材库」
@@ -334,9 +334,14 @@ export type PerfectPixelProjectVerdict =
export function inspectPerfectPixelProjectSnapshot(
project: EditorProjectSnapshot,
operation: Pick<PerfectPixelOperationSnapshot, 'operationId' | 'taskId'>,
resultResourceId?: string | null,
): PerfectPixelProjectVerdict {
const normalizedResultResourceId = resultResourceId?.trim() ?? '';
const matchingResources = project.resources.filter(
(resource) => resource.taskId?.trim() === operation.taskId,
(resource) =>
resource.taskId?.trim() === operation.taskId ||
(normalizedResultResourceId !== '' &&
resource.resourceId.trim() === normalizedResultResourceId),
);
const matchingDialogs = findCanvasGenerationDialogRecords(
project,
@@ -369,6 +374,20 @@ export function inspectPerfectPixelProjectSnapshot(
message: '完美像素占位已收口,但权威项目缺少对应任务资源。',
};
}
if (
(normalizedResultResourceId !== '' &&
resource.resourceId.trim() !== normalizedResultResourceId) ||
resource.taskId?.trim() !== operation.taskId ||
resource.projectId !== project.projectId ||
!resource.objectKey?.trim() ||
!resource.imageSrc.trim()
) {
return {
kind: 'conflict',
project,
message: '完美像素任务资源记录不完整或不属于当前项目,无法自动确认结果。',
};
}
if (!dialog) {
return { kind: 'dialog-missing', project, resource };
}
@@ -436,7 +455,7 @@ function waitForPerfectPixelReconciliationDelay(
async function reconcilePerfectPixelProject(
projectId: string,
operation: PerfectPixelOperationSnapshot,
options: { signal?: AbortSignal } = {},
options: { signal?: AbortSignal; resultResourceId?: string | null } = {},
): Promise<PerfectPixelProjectVerdict> {
let attempt = 0;
let hasAttemptedRead = false;
@@ -478,6 +497,7 @@ async function reconcilePerfectPixelProject(
const verdict = inspectPerfectPixelProjectSnapshot(
latestProject,
operation,
options.resultResourceId,
);
if (verdict.kind !== 'pending') {
return verdict;
@@ -2785,6 +2805,11 @@ export function useImageCanvasGenerationWorkflow({
error instanceof ApiClientError &&
(error.details as { operationResultAlreadyExists?: unknown } | null)
?.operationResultAlreadyExists === true;
const existingResultResourceId =
operationResultAlreadyExists && error instanceof ApiClientError
? (error.details as { resultResourceId?: unknown } | null)
?.resultResourceId
: null;
const outcomeMayBePersisted =
perfectPixelPostAttempted &&
Boolean(perfectPixelDialogId) &&
@@ -2808,6 +2833,12 @@ export function useImageCanvasGenerationWorkflow({
const verdict = await reconcilePerfectPixelProject(
normalizedProjectId,
perfectPixelOperation,
{
resultResourceId:
typeof existingResultResourceId === 'string'
? existingResultResourceId
: null,
},
);
if (!isPerfectPixelAuthorityCurrent(operationAuthority)) {
return;
@@ -3034,6 +3065,11 @@ export function useImageCanvasGenerationWorkflow({
error instanceof ApiClientError &&
(error.details as { operationResultAlreadyExists?: unknown } | null)
?.operationResultAlreadyExists === true;
const existingResultResourceId =
operationResultAlreadyExists && error instanceof ApiClientError
? (error.details as { resultResourceId?: unknown } | null)
?.resultResourceId
: null;
const outcomeMayBePersisted =
postAttempted &&
(!(error instanceof ApiClientError) ||
@@ -3044,6 +3080,12 @@ export function useImageCanvasGenerationWorkflow({
const verdict = await reconcilePerfectPixelProject(
normalizedProjectId,
retriedOperation,
{
resultResourceId:
typeof existingResultResourceId === 'string'
? existingResultResourceId
: null,
},
);
if (!isPerfectPixelAuthorityCurrent(operationAuthority)) {
return;