diff --git a/TEST.md b/TEST.md index 9815e49b..e8cc4041 100644 --- a/TEST.md +++ b/TEST.md @@ -117,7 +117,7 @@ ## T23B D601 Decision Center User Service -阅读 `AGENTS.md` 和 `docs/reference/microservices.md`,运行 `bun scripts/cli.ts microservice list`,确认 `decision-center` 显示为 `providerId=D601`、`public=false`、`frontendOnly=true`、仓库 URL `https://github.com/pikasTech/unidesk`、k3s/k8s `k3s://unidesk/decision-center:4277` 逻辑服务映射、`deployment.mode=k3sctl-managed`、`runtime.orchestrator=k3sctl` 且无业务直连容器摘要;运行 `bun scripts/cli.ts deploy apply --service decision-center --run-now`,确认命令在运行时变更前返回结构化错误,说明维护通道直连 D601 不承担服务部署;Decision Center 后续版本部署必须经未来受控 target-side CD 路径。随后运行 `bun scripts/cli.ts microservice health decision-center`,确认 `service=decision-center`、`storage=postgres`、`schemaReady=true` 且 health 中包含 `diaryEntryCount`;准备一份临时 Markdown 会议记录,运行 `bun scripts/cli.ts decision upload --title --type meeting --level G1 --status active --evidence <url>`,再运行 `bun scripts/cli.ts decision list` 和 `bun scripts/cli.ts decision show <id>`,确认 CLI 只通过 backend-core 用户服务代理访问,返回结构化 JSON 且能看到刚上传的记录。再准备一份包含 `# 2026年5月1日` 和 `# 2026年5月2日` 的临时工作日志 Markdown,运行 `bun scripts/cli.ts decision diary import <markdown-file> --source-file test-work-log.md --tag e2e`、`bun scripts/cli.ts decision diary months`、`bun scripts/cli.ts decision diary list --month 2026-05` 和 `bun scripts/cli.ts decision diary show 2026-05-01`,确认日记按 `YYYY-MM/YYYY-MM-DD.md` 虚拟路径拆分、写入 PostgreSQL 且重复导入幂等。最后登录公网 frontend `http://74.48.78.17:18081/`,进入 `用户服务 / Decision Center`,确认页面显示 G0/G1 目标、P0/P1 Blocker、停放事项、最近会议/决议、筛选、全部记录表和工作日记标签;日记标签可按月筛选并查看单日 Markdown 正文;页面不得提供聊天/LLM 会话窗口,默认不得裸 JSON,完整 JSON 只能通过 `查看原始JSON` 打开。 +阅读 `AGENTS.md` 和 `docs/reference/microservices.md`,运行 `bun scripts/cli.ts microservice list`,确认 `decision-center` 显示为 `providerId=D601`、`public=false`、`frontendOnly=true`、仓库 URL `https://github.com/pikasTech/unidesk`、k3s/k8s `k3s://unidesk/decision-center:4277` 逻辑服务映射、`deployment.mode=k3sctl-managed`、`runtime.orchestrator=k3sctl` 且无业务直连容器摘要。随后运行 `bun scripts/cli.ts microservice health decision-center`,确认 `service=decision-center`、`storage=postgres`、`schemaReady=true` 且 health 中包含 `diaryEntryCount`;准备一份临时 Markdown 会议记录,运行 `bun scripts/cli.ts decision upload <markdown-file> --title <title> --type meeting --level G1 --status active --evidence <url>`,再运行 `bun scripts/cli.ts decision list` 和 `bun scripts/cli.ts decision show <id>`,确认 CLI 只通过 backend-core 用户服务代理访问,返回结构化 JSON 且能看到刚上传的记录。再准备一份包含 `# 2026年5月1日` 和 `# 2026年5月2日` 的临时工作日志 Markdown,运行 `bun scripts/cli.ts decision diary import <markdown-file> --source-file test-work-log.md --tag e2e`、`bun scripts/cli.ts decision diary months`、`bun scripts/cli.ts decision diary list --month 2026-05`、`bun scripts/cli.ts decision diary show 2026-05-01`、`bun scripts/cli.ts decision diary upsert 2026-05-03 --body-file <markdown-file>` 和 `bun scripts/cli.ts decision diary edit 2026-05-03 --body-file <markdown-file> --tag e2e`,确认日记按 `YYYY-MM/YYYY-MM-DD.md` 虚拟路径拆分、写入 PostgreSQL、重复导入幂等且历史日记可按日期编辑。用户服务上线前的 dev 自动门禁使用 focused 集合:`bun scripts/cli.ts e2e run --only microservice:decision-center-record-crud,microservice:decision-center-diary-lifecycle,frontend:decision-center-visible,frontend:decision-center-demand-management-visible,frontend:decision-center-diary-visible`,该门禁只验证用户服务行为,不测试 CI/CD 自举,也不部署 prod。最后登录公网 frontend `http://74.48.78.17:18081/`,进入 `用户服务 / Decision Center`,确认页面显示 G0/G1 目标、P0/P1 Blocker、停放事项、最近会议/决议、需求管理工作区、需求录入编辑器、全部记录表和工作日记编辑台;日记页支持“今天”自动填入真实日期、保存当天 Markdown、编辑历史 Markdown 并再次读取一致。prod 人工验收必须在 CD 拉取已发布 artifact 后执行,逐项确认 health、records、diary editor、frontend 页面、无公网业务端口、live commit / artifact 信息;页面不得提供聊天/LLM 会话窗口,默认不得裸 JSON,完整 JSON 只能通过 `查看原始JSON` 打开。 ## T24 MET Nonlinear D601 GPU User Service diff --git a/scripts/src/e2e.ts b/scripts/src/e2e.ts index e5f7a799..e063cd92 100644 --- a/scripts/src/e2e.ts +++ b/scripts/src/e2e.ts @@ -103,6 +103,8 @@ const SERVICE_CHECK_NAMES = [ "microservice:decision-center-status", "microservice:decision-center-health", "microservice:decision-center-records", + "microservice:decision-center-record-crud", + "microservice:decision-center-diary-lifecycle", "microservice:oa-event-flow-status", "microservice:oa-event-flow-health", "microservice:oa-event-flow-diagnostics", @@ -142,6 +144,8 @@ const FRONTEND_CHECK_NAMES = [ "frontend:findjob-integrated-visible", "frontend:oa-event-flow-visible", "frontend:decision-center-visible", + "frontend:decision-center-demand-management-visible", + "frontend:decision-center-diary-visible", "frontend:code-queue-integrated-visible", "frontend:code-queue-workdirs-loaded", "frontend:code-queue-enqueue-await-smoke", @@ -1385,6 +1389,66 @@ async function serviceChecks(config: UniDeskConfig, urls: PublicUrls, checks: E2 const decisionCenterStatus = apiClient.getJson("/api/microservices/decision-center/status"); const decisionCenterHealth = apiClient.getJson("/api/microservices/decision-center/health"); const decisionCenterRecords = apiClient.getJson("/api/microservices/decision-center/proxy/api/records?limit=20"); + const decisionCenterRecordMarker = `E2E Decision CRUD ${Date.now()}`; + const decisionCenterRecordCreate = wantsCheck(options, "microservice:decision-center-record-crud") + ? apiClient.getJson("/api/microservices/decision-center/proxy/api/records", { + method: "POST", + body: { + type: "goal", + level: "G0", + status: "active", + title: decisionCenterRecordMarker, + body: "E2E creates, edits and deletes a Decision Center requirement record.", + tags: ["e2e", "external-goal"], + evidenceLinks: ["https://example.com/unidesk/decision-center-record-crud"], + }, + }) + : null; + const decisionCenterRecordId = String((decisionCenterRecordCreate as { body?: { record?: { id?: string } } } | null)?.body?.record?.id || ""); + const decisionCenterRecordUpdate = decisionCenterRecordId + ? apiClient.getJson(`/api/microservices/decision-center/proxy/api/records/${encodeURIComponent(decisionCenterRecordId)}`, { + method: "PUT", + body: { + status: "blocked", + level: "P1", + body: "E2E edited this Decision Center requirement record before deletion.", + tags: ["e2e", "blocked"], + }, + }) + : { ok: false, error: "missing Decision Center record id", create: decisionCenterRecordCreate }; + const decisionCenterRecordRead = decisionCenterRecordId + ? apiClient.getJson(`/api/microservices/decision-center/proxy/api/records/${encodeURIComponent(decisionCenterRecordId)}`) + : { ok: false, error: "missing Decision Center record id", create: decisionCenterRecordCreate }; + const decisionCenterRecordDelete = decisionCenterRecordId + ? apiClient.getJson(`/api/microservices/decision-center/proxy/api/records/${encodeURIComponent(decisionCenterRecordId)}`, { method: "DELETE" }) + : { ok: false, error: "missing Decision Center record id", create: decisionCenterRecordCreate }; + const decisionCenterDiaryDate = "2099-12-31"; + const decisionCenterDiaryMarker = `Decision Center diary lifecycle ${Date.now()}`; + const decisionCenterDiaryCreate = wantsCheck(options, "microservice:decision-center-diary-lifecycle") + ? apiClient.getJson(`/api/microservices/decision-center/proxy/api/diary/entries/${decisionCenterDiaryDate}`, { + method: "PUT", + body: { + title: "E2E Decision Center Diary", + body: `# ${decisionCenterDiaryDate}\n${decisionCenterDiaryMarker}\n`, + sourceFile: "e2e-decision-center.md", + tags: ["e2e", "diary"], + }, + }) + : null; + const decisionCenterDiaryEdit = decisionCenterDiaryCreate + ? apiClient.getJson(`/api/microservices/decision-center/proxy/api/diary/entries/${decisionCenterDiaryDate}`, { + method: "PUT", + body: { + title: "E2E Decision Center Diary Edited", + body: `# ${decisionCenterDiaryDate}\n${decisionCenterDiaryMarker}\nEdited.\n`, + sourceFile: "e2e-decision-center.md", + tags: ["e2e", "diary", "edited"], + }, + }) + : null; + const decisionCenterDiaryRead = decisionCenterDiaryCreate + ? apiClient.getJson(`/api/microservices/decision-center/proxy/api/diary/entries/${decisionCenterDiaryDate}`) + : null; const filebrowserHealth = apiClient.getJson("/api/microservices/filebrowser/health"); const filebrowserWebui = apiClient.getJson("/api/microservices/filebrowser/proxy/"); const filebrowserD601Health = apiClient.getJson("/api/microservices/filebrowser-d601/health"); @@ -1676,6 +1740,38 @@ async function serviceChecks(config: UniDeskConfig, urls: PublicUrls, checks: E2 addSelectedCheck(checks, options, "microservice:decision-center-status", (decisionCenterStatus as { ok?: boolean }).ok === true && (decisionCenterStatus as { body?: { microservice?: { id?: string; providerId?: string } } }).body?.microservice?.providerId === "D601", decisionCenterStatus); addSelectedCheck(checks, options, "microservice:decision-center-health", (decisionCenterHealth as { ok?: boolean }).ok === true && decisionCenterHealthBody?.ok === true && decisionCenterHealthBody.service === "decision-center" && decisionCenterHealthBody.storage === "postgres" && decisionCenterHealthBody.schemaReady === true, decisionCenterHealth); addSelectedCheck(checks, options, "microservice:decision-center-records", (decisionCenterRecords as { ok?: boolean }).ok === true && decisionCenterRecordsBody?.ok === true && Array.isArray(decisionCenterRecordsBody.records), decisionCenterRecords); + addSelectedCheck(checks, options, "microservice:decision-center-record-crud", + (decisionCenterRecordCreate as { ok?: boolean } | null)?.ok === true + && (decisionCenterRecordUpdate as { ok?: boolean }).ok === true + && (decisionCenterRecordRead as { ok?: boolean; body?: { record?: { id?: string; status?: string; level?: string; body?: string } } }).ok === true + && (decisionCenterRecordRead as { body?: { record?: { id?: string; status?: string; level?: string; body?: string } } }).body?.record?.id === decisionCenterRecordId + && (decisionCenterRecordRead as { body?: { record?: { status?: string; level?: string; body?: string } } }).body?.record?.status === "blocked" + && (decisionCenterRecordRead as { body?: { record?: { status?: string; level?: string; body?: string } } }).body?.record?.level === "P1" + && String((decisionCenterRecordRead as { body?: { record?: { body?: string } } }).body?.record?.body || "").includes("E2E edited") + && (decisionCenterRecordDelete as { ok?: boolean }).ok === true, + { + marker: decisionCenterRecordMarker, + recordId: decisionCenterRecordId, + create: decisionCenterRecordCreate, + update: decisionCenterRecordUpdate, + read: decisionCenterRecordRead, + delete: decisionCenterRecordDelete, + }); + addSelectedCheck(checks, options, "microservice:decision-center-diary-lifecycle", + (decisionCenterDiaryCreate as { ok?: boolean } | null)?.ok === true + && (decisionCenterDiaryEdit as { ok?: boolean } | null)?.ok === true + && (decisionCenterDiaryRead as { ok?: boolean; body?: { entry?: { date?: string; title?: string; body?: string; sourceFile?: string; tags?: string[] } } } | null)?.ok === true + && (decisionCenterDiaryRead as { body?: { entry?: { date?: string; title?: string; body?: string; sourceFile?: string; tags?: string[] } } } | null)?.body?.entry?.date === decisionCenterDiaryDate + && String((decisionCenterDiaryRead as { body?: { entry?: { title?: string; body?: string } } } | null)?.body?.entry?.title || "").includes("Edited") + && String((decisionCenterDiaryRead as { body?: { entry?: { title?: string; body?: string } } } | null)?.body?.entry?.body || "").includes(decisionCenterDiaryMarker) + && String((decisionCenterDiaryRead as { body?: { entry?: { title?: string; body?: string } } } | null)?.body?.entry?.body || "").includes("Edited."), + { + date: decisionCenterDiaryDate, + marker: decisionCenterDiaryMarker, + create: decisionCenterDiaryCreate, + edit: decisionCenterDiaryEdit, + read: decisionCenterDiaryRead, + }); const upgradeDispatch = dockerCoreJson("/api/dispatch", { method: "POST", body: { providerId: config.providerGateway.id, command: "provider.upgrade", payload: { source: "cli-e2e", mode: "plan" } }, @@ -1775,7 +1871,11 @@ async function frontendCheck(config: UniDeskConfig, urls: PublicUrls, checks: E2 const needTodoNote = wants("frontend:todo-note-integrated-visible"); const needFindJob = wants("frontend:findjob-integrated-visible"); const needOaEventFlow = wants("frontend:oa-event-flow-visible"); - const needDecisionCenter = wants("frontend:decision-center-visible"); + const needDecisionCenter = wantsAny([ + "frontend:decision-center-visible", + "frontend:decision-center-demand-management-visible", + "frontend:decision-center-diary-visible", + ]); const needCodeQueue = wantsAny([ "frontend:code-queue-integrated-visible", "frontend:code-queue-workdirs-loaded", @@ -1880,6 +1980,10 @@ async function frontendCheck(config: UniDeskConfig, urls: PublicUrls, checks: E2 let decisionCenterE2eRecord: any = null; let decisionCenterDeleteResult: any = null; let decisionCenterMetrics: any = { pageVisible: false, tableVisible: false, rawButtonCount: 0, rawJsonBlocks: 0, chatInputCount: 0, bodyContainsRecord: false }; + let decisionCenterDemandText = ""; + let decisionCenterDemandMetrics: any = { workspaceVisible: false, editorVisible: false, lanes: {}, filterButtons: 0 }; + let decisionCenterDiaryText = ""; + let decisionCenterDiaryMetrics: any = { editorVisible: false, todayButtonVisible: false, dateValue: "", bodyValue: "", entryCards: 0, selectedHasMarker: false, duplicateDateCards: 0 }; let codeQueueText = ""; let codeQueueOutputText = ""; let codeQueueTaskCount = 0; @@ -2187,8 +2291,12 @@ async function frontendCheck(config: UniDeskConfig, urls: PublicUrls, checks: E2 }); } if (needDecisionCenter) { - const decisionCenterE2eTitle = `E2E Decision Center ${Date.now()}`; - decisionCenterE2eRecord = await page.evaluate(async (title) => { + const decisionCenterMarker = Date.now(); + const decisionCenterE2eTitle = `E2E Decision Center ${decisionCenterMarker}`; + const decisionCenterExternalGoalTitle = `E2E External Goal ${decisionCenterMarker}`; + const decisionCenterDiaryDate = "2099-12-30"; + const decisionCenterDiaryMarker = `E2E diary marker ${decisionCenterMarker}`; + decisionCenterE2eRecord = await page.evaluate(async ({ title, externalGoalTitle }) => { const response = await fetch("/api/microservices/decision-center/proxy/api/records", { method: "POST", credentials: "same-origin", @@ -2203,8 +2311,30 @@ async function frontendCheck(config: UniDeskConfig, urls: PublicUrls, checks: E2 evidenceLinks: ["https://example.com/unidesk/decision-center-e2e"], }), }); - return { ok: response.ok, status: response.status, body: await response.json().catch(() => null) }; - }, decisionCenterE2eTitle); + const meeting = await response.json().catch(() => null); + const goalResponse = await fetch("/api/microservices/decision-center/proxy/api/records", { + method: "POST", + credentials: "same-origin", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ + type: "goal", + level: "G0", + status: "active", + title: externalGoalTitle, + body: "E2E seeded external goal for demand-management visibility.", + tags: ["e2e", "external-goal"], + }), + }); + const goal = await goalResponse.json().catch(() => null); + return { + ok: response.ok && goalResponse.ok, + status: response.status, + body: meeting, + goalStatus: goalResponse.status, + goal, + }; + }, { title: decisionCenterE2eTitle, externalGoalTitle: decisionCenterExternalGoalTitle }); + const decisionCenterE2eGoalId = String(decisionCenterE2eRecord?.goal?.record?.id || ""); await page.getByRole("button", { name: /Decision Center/ }).click(); await page.waitForSelector('[data-testid="decision-center-page"]', { timeout: 10000 }); await page.waitForSelector('[data-testid="decision-center-filters"]', { timeout: 30000 }); @@ -2219,6 +2349,7 @@ async function frontendCheck(config: UniDeskConfig, urls: PublicUrls, checks: E2 && text.includes("查看原始JSON") && text.includes(String(title)); }, decisionCenterE2eTitle, { timeout: 30000 }); + await page.waitForSelector('[data-testid="requirement-workspace"]', { timeout: 10000 }); decisionCenterText = await page.locator('[data-testid="decision-center-page"]').innerText({ timeout: 5000 }); decisionCenterMetrics = await page.evaluate(() => { const root = document.querySelector('[data-testid="decision-center-page"]') as HTMLElement | null; @@ -2234,6 +2365,50 @@ async function frontendCheck(config: UniDeskConfig, urls: PublicUrls, checks: E2 textPreview: root?.innerText.slice(0, 1000) || "", }; }); + decisionCenterDemandText = await page.locator('[data-testid="decision-center-page"]').innerText({ timeout: 5000 }); + decisionCenterDemandMetrics = await page.evaluate(() => { + const root = document.querySelector('[data-testid="decision-center-page"]') as HTMLElement | null; + const workspace = root?.querySelector('[data-testid="requirement-workspace"]') as HTMLElement | null; + const laneIds = ["external-goal", "internal-goal", "blocker", "parked", "authority"]; + const lanes = Object.fromEntries(laneIds.map((id) => [id, Boolean(root?.querySelector(`[data-testid="requirement-lane-${id}"]`))])); + return { + workspaceVisible: Boolean(workspace), + editorVisible: Boolean(root?.querySelector('[data-testid="record-editor"]')), + filtersVisible: Boolean(root?.querySelector('[data-testid="decision-center-filters"]')), + filterButtons: root?.querySelectorAll('[data-testid^="requirement-filter-"]').length ?? 0, + lanes, + tableRows: root?.querySelectorAll('[data-testid="decision-center-record-table"] tbody tr').length ?? 0, + textPreview: root?.innerText.slice(0, 1000) || "", + }; + }); + await page.getByTestId("decision-tab-diary").click(); + await page.waitForSelector('[data-testid="diary-editor"]', { timeout: 10000 }); + await page.getByTestId("today-diary").click(); + await page.waitForFunction(() => { + const input = document.querySelector('[data-testid="diary-date-input"]') as HTMLInputElement | null; + return /^\d{4}-\d{2}-\d{2}$/u.test(input?.value || ""); + }, undefined, { timeout: 10000 }); + await page.locator('[data-testid="diary-date-input"]').fill(decisionCenterDiaryDate); + await page.locator('[data-testid="diary-body-editor"]').fill(`# ${decisionCenterDiaryDate}\n${decisionCenterDiaryMarker}\n`); + await page.getByTestId("save-diary-button").click(); + await page.waitForFunction((marker) => document.body.innerText.includes(String(marker)), decisionCenterDiaryMarker, { timeout: 30000 }); + decisionCenterDiaryText = await page.locator('[data-testid="decision-center-page"]').innerText({ timeout: 5000 }); + decisionCenterDiaryMetrics = await page.evaluate(({ date, marker }) => { + const root = document.querySelector('[data-testid="decision-center-page"]') as HTMLElement | null; + const dateInput = root?.querySelector('[data-testid="diary-date-input"]') as HTMLInputElement | null; + const bodyInput = root?.querySelector('[data-testid="diary-body-editor"]') as HTMLTextAreaElement | null; + const dateCards = Array.from(root?.querySelectorAll(`[data-testid="diary-entry-${date}"]`) ?? []); + return { + editorVisible: Boolean(root?.querySelector('[data-testid="diary-editor"]')), + todayButtonVisible: Boolean(root?.querySelector('[data-testid="today-diary"]')) && Boolean(root?.querySelector('[data-testid="today-diary-button"]')), + dateValue: dateInput?.value || "", + bodyValue: bodyInput?.value || "", + entryCards: root?.querySelectorAll('[data-testid^="diary-entry-"]').length ?? 0, + duplicateDateCards: dateCards.length, + selectedHasMarker: Boolean(root?.innerText.includes(String(marker))), + textPreview: root?.innerText.slice(0, 1000) || "", + }; + }, { date: decisionCenterDiaryDate, marker: decisionCenterDiaryMarker }); const decisionCenterRecordId = String(decisionCenterE2eRecord?.body?.record?.id || ""); if (decisionCenterRecordId) { decisionCenterDeleteResult = await page.evaluate(async (id) => { @@ -2244,6 +2419,14 @@ async function frontendCheck(config: UniDeskConfig, urls: PublicUrls, checks: E2 return { ok: response.ok, status: response.status, body: await response.json().catch(() => null) }; }, decisionCenterRecordId); } + if (decisionCenterE2eGoalId) { + await page.evaluate(async (id) => { + await fetch(`/api/microservices/decision-center/proxy/api/records/${encodeURIComponent(String(id))}`, { + method: "DELETE", + credentials: "same-origin", + }).catch(() => null); + }, decisionCenterE2eGoalId); + } } if (needCodeQueue) { await page.getByLabel("用户服务 子功能").getByRole("button", { name: "Code Queue" }).click(); @@ -3363,6 +3546,39 @@ async function frontendCheck(config: UniDeskConfig, urls: PublicUrls, checks: E2 && Number(decisionCenterMetrics.chatInputCount || 0) === 0 && !decisionCenterText.includes("{\n"), { decisionCenterMetrics, decisionCenterE2eRecord, decisionCenterDeleteResult, decisionCenterTextPreview: decisionCenterText.slice(0, 1400) }); + addSelectedCheck(checks, options, "frontend:decision-center-demand-management-visible", + decisionCenterDemandText.includes("需求管理工作区") + && decisionCenterDemandText.includes("外部目标") + && decisionCenterDemandText.includes("内部目标") + && decisionCenterDemandText.includes("阻塞") + && decisionCenterDemandText.includes("停放事项") + && decisionCenterDemandText.includes("决议/实验/债务") + && decisionCenterDemandText.includes("录入需求记录") + && decisionCenterDemandMetrics.workspaceVisible === true + && decisionCenterDemandMetrics.editorVisible === true + && decisionCenterDemandMetrics.filtersVisible === true + && Number(decisionCenterDemandMetrics.filterButtons || 0) >= 5 + && decisionCenterDemandMetrics.lanes?.["external-goal"] === true + && decisionCenterDemandMetrics.lanes?.["internal-goal"] === true + && decisionCenterDemandMetrics.lanes?.blocker === true + && decisionCenterDemandMetrics.lanes?.parked === true + && decisionCenterDemandMetrics.lanes?.authority === true + && !decisionCenterDemandText.includes("{\n"), + { decisionCenterDemandMetrics, decisionCenterDemandPreview: decisionCenterDemandText.slice(0, 1400) }); + addSelectedCheck(checks, options, "frontend:decision-center-diary-visible", + decisionCenterDiaryText.includes("工作日记编辑台") + && decisionCenterDiaryText.includes("日记筛选") + && decisionCenterDiaryText.includes("按天条目") + && decisionCenterDiaryText.includes("PostgreSQL / YYYY-MM/YYYY-MM-DD.md") + && decisionCenterDiaryMetrics.editorVisible === true + && decisionCenterDiaryMetrics.todayButtonVisible === true + && decisionCenterDiaryMetrics.dateValue === "2099-12-30" + && String(decisionCenterDiaryMetrics.bodyValue || "").includes("E2E diary marker") + && Number(decisionCenterDiaryMetrics.entryCards || 0) >= 1 + && Number(decisionCenterDiaryMetrics.duplicateDateCards || 0) >= 1 + && decisionCenterDiaryMetrics.selectedHasMarker === true + && !decisionCenterDiaryText.includes("{\n"), + { decisionCenterDiaryMetrics, decisionCenterDiaryPreview: decisionCenterDiaryText.slice(0, 1400) }); addSelectedCheck(checks, options, "frontend:code-queue-integrated-visible", codeQueueTextLower.includes("code queue") && codeQueueText.includes("gpt-5.4-mini") && codeQueueText.includes("gpt-5.4") && codeQueueText.includes("gpt-5.5") && codeQueueText.includes("提交任务") && codeQueueText.includes("执行 Provider") && codeQueueText.includes("入队份数") && codeQueueText.includes("追加 prompt") && codeQueueText.includes("打断") && codeQueueTextLower.includes("查看 queue") && codeQueueText.includes("创建 queue") && codeQueueText.includes("合并 queue") && codeQueueOptions.some((text) => text.includes("All queues")) && codeQueueTracePlacement.firstChildIsTrace === true && codeQueueTracePlacement.noPageTopStatus === true && codeQueueTracePlacement.filterInsideTracePanel === true && codeQueueTracePlacement.taskSearchVisible === true && codeQueueTracePlacement.traceStatusVisible === true && codeQueueTracePlacement.markAllReadVisible === true && codeQueueGlobalStatus.activeMicroserviceVisible === true && codeQueueSidebarUpdateMetrics.hasRecentUpdateLabel === true && codeQueueHtmlGuard.rootAttrMissing === true && codeQueueHtmlGuard.sourceAttrMissing === true && codeQueueHtmlGuard.sourceNoBasePrompt === true && codeQueueSubmitQueueControl.tagName === "select" && codeQueueSubmitQueueControl.createButtonVisible === true && codeQueueSubmitQueueControl.mergeButtonVisible === true && codeQueueSubmitQueueControl.mergeSourceInlineMissing === true && codeQueueSubmitQueueControl.mergeDialogMissingBeforeClick === true && (codeQueueSubmitQueueControl.mergeButtonDisabled === true || (codeQueueSubmitQueueControl.mergeDialogVisible === true && codeQueueSubmitQueueControl.mergeDialogSelectVisible === true && Number(codeQueueSubmitQueueControl.mergeDialogSourceOptionCount || 0) > 1 && codeQueueSubmitQueueControl.mergeDialogSelectInsideSubmitForm !== true && codeQueueSubmitQueueControl.mergeDialogUsesCommonComponent === true && codeQueueSubmitQueueControl.mergeDialogDeleteNoteVisible === true)) && codeQueueSubmitQueueControl.oldInputMissing === true && codeQueueSubmitQueueControl.providerValue === "D601" && codeQueueSubmitQueueControl.cwdValue === "/workspace" && Array.isArray(codeQueueSubmitQueueControl.providerOptions) && codeQueueSubmitQueueControl.providerOptions.some((item: any) => item.value === "D601" && String(item.text || "").includes("/workspace")) && codeQueueSubmitQueueControl.maxAttemptsMax === "99" && codeQueueSubmitQueueControl.maxAttemptsValue === "99" && codeQueueSubmitQueueControl.moveQueueVisible === true && codeQueuePromptDefaultEmpty === true && codeQueueSubmitGuard.batchRowVisible === true && codeQueueSubmitGuard.checkboxVisible === true && codeQueueSubmitGuard.disabledBeforeConfirm === true && codeQueueSubmitGuard.enabledAfterConfirm === true && codeQueueSubmitGuard.waitElementMissingBeforeSubmit === true && codeQueueScrollbarMetrics.transcriptThin === true && codeQueueScrollbarMetrics.toolHorizontalHidden === true && (codeQueueSwitchMetrics.optionCount <= 1 || codeQueueSwitchMetrics.switched === true) && codeQueueTextLower.includes("attempts") && codeQueueText.includes("仅 UniDesk frontend 代理访问") && (codeQueueTaskCount === 0 || codeQueueOutputText.includes("Submitted prompt")), { codeQueueTaskCount, codeQueueOptions, codeQueueSwitchMetrics, codeQueueSubmitQueueControl, codeQueueSubmitGuard, codeQueueScrollbarMetrics, codeQueuePromptDefaultEmpty, codeQueueTracePlacement, codeQueueGlobalStatus, codeQueueSidebarUpdateMetrics, codeQueueHtmlGuard, codeQueueOutputPreview: codeQueueOutputText.slice(0, 900), codeQueueTextPreview: codeQueueText.slice(0, 1400) }); const expectedFrontendCodeQueueProvider = isDevFrontendTarget() ? "D601-dev" : "D601"; const expectedFrontendCodeQueueWorkdir = isDevFrontendTarget() ? "/workspace-dev" : "/workspace";