fix(onboard/parity): 处置 P8g 独立审查——新建入口按有效根目录弹出、Toast 置于面板之上、bypass 与错误阶段文案如实
All checks were successful
Push & PR Check / check (push) Successful in 1m2s
All checks were successful
Push & PR Check / check (push) Successful in 1m2s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
5b6c197a74
commit
9d85609287
@ -338,6 +338,9 @@ skill 只是五个接入面之一,兼容层覆盖全部五面。
|
|||||||
- depends: P8e
|
- depends: P8e
|
||||||
- acceptance: "agent_verify: [pnpm typecheck && pnpm test] 视图纯函数:blocked 非空时确认按钮禁用并列出文件和原因;not_installed 和 not_applicable 显示为灰色说明、不显示为红色失败;bypassPermissions 和 Codex 信任单独列出;确认时携带 expected_digest。human_verify: ① 用临时 git 仓库新建项目,弹出接入预览;② 确认后 30 秒内看到分步结果、L1 矩阵和「待你提交的 N 个文件」;③ 另试一次在预览里取消,目录零改动;④ 在该目录新开 Claude Code、Codex、DSH 桌面 GUI 会话调 LJ_PROBE_NONEXISTENT,得「开发需求未登记」。"
|
- acceptance: "agent_verify: [pnpm typecheck && pnpm test] 视图纯函数:blocked 非空时确认按钮禁用并列出文件和原因;not_installed 和 not_applicable 显示为灰色说明、不显示为红色失败;bypassPermissions 和 Codex 信任单独列出;确认时携带 expected_digest。human_verify: ① 用临时 git 仓库新建项目,弹出接入预览;② 确认后 30 秒内看到分步结果、L1 矩阵和「待你提交的 N 个文件」;③ 另试一次在预览里取消,目录零改动;④ 在该目录新开 Claude Code、Codex、DSH 桌面 GUI 会话调 LJ_PROBE_NONEXISTENT,得「开发需求未登记」。"
|
||||||
- 说明:新建 / 模板创建成功后弹出「接入预览」,create_project 本身保持无副作用;批量添加只提供批量入口、不自动接入;BlueprintModal 的「应用接入包」改为先 plan 再 apply。
|
- 说明:新建 / 模板创建成功后弹出「接入预览」,create_project 本身保持无副作用;批量添加只提供批量入口、不自动接入;BlueprintModal 的「应用接入包」改为先 plan 再 apply。
|
||||||
|
- 实现(2026-10-04,分支 feat/agent-client-parity-p8g,统筹会话主线程直做——两次委派卡死于一次性生成大文件):onboardView.ts 视图纯函数(计划预览、确认请求、报告、L1 矩阵;判定全取后端字段);OnboardPanel.tsx 全局挂载于 App、经 useUIStore.openOnboard 打开(新建弹窗保存即卸载,面板须独立存在),状态机 planning → preview → applying → report,过期响应按请求序号丢弃、接入中不可关闭,refused 附新计划可回预览重新确认;plan.rs 的 BlockedItem 序列化附派生字段 skippable(可跳过判定只在本体,反序列化忽略、不进 digest),commands.ts 同步;入口:ProjectModal 新建且按 platform 的有效根目录非空才弹(概念占位不弹),NewProjectModal 模板创建成功自动弹、完成页可再开,BatchAddModal 添加后列出有目录的新项目逐个「一键接入」,BlueprintModal「施加 Buff」改为同一面板(buff 已激活时另给「重新检查接入」),关闭后按报告刷新 buff 与 init prompt;project-context.yaml 同步 skippable 与 UI 投影。
|
||||||
|
- 有意偏差:"① 呈现块拆到 OnboardSections.tsx(files 外新增,控制单文件规模)。② Toast 层级 z-50 → z-[70](审查 ②,否则接入提示被面板遮住)。③ 本地 confirm 函数改名 applyConfirmed:confirmUsage.test.ts 扫描 confirm( 防同步确认框,属正确的守卫。"
|
||||||
|
- review: "2026-10-04 独立只读审查 5b6c197,结论 merge,5 条:① [低] 新建入口按任一路径非空判是否弹面板,与后端按 platform 取有效根目录不同口径 → 已修。② [低] Toast z-50 被面板 z-[60] 遮住 → 已修。③ [低] 已有 permissions 又取消 bypass 时文案称会写 default → 已改文案(如实说明两种情况)。④ [信息] L1 格子着色在前端写死 codex_project_untrusted=待你操作,轻度复制判定 → 转后续小修(L1 格子带 pending_user 字段)。⑤ [信息] onboard_project 抛错后「取消」提示仍称不写入 → 已修(错误阶段换提示)。审查核实:确认带 plan.digest 与生成计划时的选项;refused 回预览时 planned 与新计划同一组选项(apply.rs 用请求选项重算);四条 agent_verify 有实断言。"
|
||||||
|
|
||||||
### ✅ P9 — DSH 桌面 GUI 按工作区连项目地址(会话级插件)
|
### ✅ P9 — DSH 桌面 GUI 按工作区连项目地址(会话级插件)
|
||||||
- status: done
|
- status: done
|
||||||
|
|||||||
@ -165,7 +165,7 @@ function PanelFooter({
|
|||||||
onClick={onCancel}
|
onClick={onCancel}
|
||||||
disabled={phase.kind === "applying"}
|
disabled={phase.kind === "applying"}
|
||||||
className={`${btnCls} border-gray-200 text-gray-600 hover:bg-gray-50`}
|
className={`${btnCls} border-gray-200 text-gray-600 hover:bg-gray-50`}
|
||||||
title="取消不写入任何文件"
|
title={phase.kind === "error" ? "关闭面板;若错误发生在确认接入之后,可能已有部分写入,请重新计划核对" : "取消不写入任何文件"}
|
||||||
>
|
>
|
||||||
取消
|
取消
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@ -95,7 +95,9 @@ const pickFolder = async (field: "wsl_path" | "win_path") => {
|
|||||||
qc.invalidateQueries({ queryKey: ["tagMaps"] });
|
qc.invalidateQueries({ queryKey: ["tagMaps"] });
|
||||||
closeEdit();
|
closeEdit();
|
||||||
// create_project 本身无副作用;选了目录的新项目弹出接入预览(P8g),由人确认后才写入。不选目录的概念占位不接入
|
// create_project 本身无副作用;选了目录的新项目弹出接入预览(P8g),由人确认后才写入。不选目录的概念占位不接入
|
||||||
if (isNew && (form.win_path?.trim() || form.wsl_path?.trim())) {
|
// 与后端登记同口径:按 platform 只看有效根目录那一列(另一列残留不算)
|
||||||
|
const effectiveRoot = form.platform === "wsl" ? form.wsl_path : form.win_path;
|
||||||
|
if (isNew && effectiveRoot?.trim()) {
|
||||||
openOnboard({ projectId: form.id!, projectName: form.name?.trim() });
|
openOnboard({ projectId: form.id!, projectName: form.name?.trim() });
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@ -279,7 +279,8 @@ function bypassView(plan: OnboardingPlan, planned: OnboardSelection, selection:
|
|||||||
return {
|
return {
|
||||||
...base,
|
...base,
|
||||||
label: "已按项目取消 bypassPermissions",
|
label: "已按项目取消 bypassPermissions",
|
||||||
detail: "settings.json 缺 permissions 时写显式 defaultMode=default 记住选择,之后的重注入不会补回。",
|
detail:
|
||||||
|
"不写入 bypassPermissions。settings.json 缺 permissions 时会写显式 defaultMode=default 记住选择(之后的重注入不会补回);已有 permissions 则原样不动。",
|
||||||
tone: "muted",
|
tone: "muted",
|
||||||
toggleable: true,
|
toggleable: true,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,7 +7,7 @@ export function Toast() {
|
|||||||
const isError = toast.startsWith("❌");
|
const isError = toast.startsWith("❌");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`fixed bottom-10 left-1/2 -translate-x-1/2 z-50 text-sm px-4 py-2 rounded-lg shadow-lg animate-fade-in flex items-center gap-2 max-w-[80vw] ${
|
<div className={`fixed bottom-10 left-1/2 -translate-x-1/2 z-[70] text-sm px-4 py-2 rounded-lg shadow-lg animate-fade-in flex items-center gap-2 max-w-[80vw] ${
|
||||||
isError ? "bg-red-900 text-white pointer-events-auto" : "bg-gray-900 text-white pointer-events-none"
|
isError ? "bg-red-900 text-white pointer-events-auto" : "bg-gray-900 text-white pointer-events-none"
|
||||||
}`}>
|
}`}>
|
||||||
<span className="break-all">{toast}</span>
|
<span className="break-all">{toast}</span>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user