fix: avoid stray zero in consultation composer

This commit is contained in:
2026-09-24 13:51:34 +08:00
parent 0a8fce67a4
commit 3c68155ecd
4 changed files with 57 additions and 1 deletions

View File

@@ -0,0 +1,42 @@
# Task: Fix consultation response rendering
## Identity
- Task ID: 20260924-agent-rendering-a8c2d419
- Mode: Feature
- Branch: codex/20260924-agent-rendering-a8c2d419-agent-rendering
- Worktree: D:\Datas\OthersProjects\.codex-worktrees\makelore\20260924-agent-rendering-a8c2d419
- Base commit: 0a8fce67a4ea6de291f82c09c17d3efe0ea7b92c
- Owner: codex
- Status: Ready for Integration
## Scope
- 修复用户截图中的咨询面板渲染:空旧草稿导致数字 0;定位序列化项目上下文进入智能体回复的问题。客户端仅修改证据确认的显示缺陷,云端事件生产问题在独立 Yuxi 任务处理。
## Intent And Constraints
- maintain-project-docs 与 diagnosing-bugs 已加载。官方 check/start/status、项目记忆与 peer scope 检查完成,Concurrent/Planning Gates Passed;Feature 模式仅写本人记录和必要产品/测试文件。
- 保留用户话题原文,不批量替换转义字符,不以 JSON 外形猜测并删除回复。线上事件探针被自动审批以 blocked by policy 拒绝,已停止该访问方式,改用本地数据和真实协议测试。用户随后批准创建 1 个全新只读 Reviewer,已委派两端补丁审查。
## Outcome
- 已从本地历史确认异常回复含序列化上下文;Yuxi 对非 AI 消息的错误投影在关联任务修复,客户端不添加猜测式内容清洗。
- 空旧草稿条件显式转换为 Boolean,避免 references.length 为 0 时作为 React 子节点显示。保留有内容的旧草稿入口,产品流程不变;README 无需改变。
## Verification
- pnpm exec vitest run tests/unit/coding-teacher-ui.test.tsx -t 'does not render a zero' --maxWorkers=1:预期失败,复现截图数字 0。
- pnpm exec vitest run tests/unit/coding-teacher-ui.test.tsx --maxWorkers=1:30 passed。
- MAKELORE_LAYOUT_BROWSER_CHANNEL=msedge pnpm exec playwright test tests/e2e/teacher-discussion-layout.spec.ts --grep ordinary:319/508px 两个真实浏览器页面用例通过,使用现有隔离页面与模拟 API。
- pnpm run typecheck、三处改动文件的 eslint、pnpm run build:vite、git diff --check:通过。构建仅有既有 chunk 大小及动态/静态混用提示。
- 官方 check_doc_drift.py:Feature 文件所有权边界通过。未打包、安装、合并、推送或部署;未进行线上收费对话。
- 用户授权的全新只读 Reviewer agent_rendering_review 独立审查 PASS,无 findings;复跑客户端 30/30、Yuxi 55/55 通过。已目视核对508px页面截图,无数字0,回复/草稿/快捷提问显示正常。
## Follow-ups
- 部署 Yuxi 并更新客户端后才能影响用户当前安装,部署后验收一次真实提问及只读工具续接。历史异常原文保持不变。当前仅交付任务分支修复,不含合并或部署。
## Promotion Candidates
- None recorded.

View File

@@ -443,7 +443,7 @@ export function TeacherChatPanel({
{topic?.unsaved && <div className="px-5 py-2 text-xs text-destructive">回复尚未保存,请复制或重试保存。<button className="ml-2 underline" onClick={() => void teacherApi.save(topicBase, topic.id).then(consume).catch(() => setError('保存失败,请先复制回复。'))}>重试保存</button></div>}
{!topicEnabled && !error && !busy && <p className="px-5 pb-2 text-xs text-muted-foreground">{legacyFriend ? '这是旧版内置朋友的话题,仅供查看。请选择智能体新建话题。' : `${label}暂未开放,历史仍可查看。`}</p>}
<form className="px-4 pb-4 pt-2" onSubmit={(e) => { e.preventDefault(); void send(); }}>
{(legacyDraft.text?.trim() || legacyDraft.references?.length) && <details className="mb-2 text-xs text-muted-foreground">
{Boolean(legacyDraft.text?.trim() || legacyDraft.references?.length) && <details className="mb-2 text-xs text-muted-foreground">
<summary>查看旧版朋友草稿</summary>
<div className="max-h-40 overflow-auto whitespace-pre-wrap rounded-lg border p-2">
{legacyDraft.text}

View File

@@ -24,9 +24,14 @@ for (const width of [319, 508]) for (const kind of ['ordinary', 'ideas', 'struct
test(`${kind} pins the current tool and composer at ${width}px`, async ({ page }) => {
await page.setViewportSize({ width: 1180, height: 800 });
const errors: string[] = []; page.on('pageerror', error => errors.push(error.message));
await page.addInitScript(() => localStorage.setItem(
'makelore-consultation-draft:fixture:fixture:friend', JSON.stringify({ text: '', references: [] }),
));
await page.goto(`${url}?kind=${kind}&width=${width}`);
const input = page.getByRole('textbox', { name: '向智能体提问' });
await expect(input).toBeEnabled();
await expect(page.locator('form')).not.toHaveText(/^0/);
await expect(page.getByText('查看旧版朋友草稿')).toHaveCount(0);
const tool = page.getByTestId('teacher-discussion');
if (kind === 'ordinary') {
await expect(tool).toHaveCount(0);

View File

@@ -96,6 +96,15 @@ async function ready() {
await waitFor(() => expect(streams.has('first')).toBe(true));
}
describe('teacher side chat', () => {
it('does not render a zero for an empty legacy friend draft', async () => {
localStorage.setItem('makelore-consultation-draft:signed-out:p:friend', JSON.stringify({ text: '', references: [] }));
render(<TeacherChatPanel projectId="p" sourceId="c" />);
await ready();
const composer = screen.getByLabelText('向智能体提问').closest('form');
expect(composer).not.toHaveTextContent(/^0/);
expect(screen.queryByText('查看旧版朋友草稿')).not.toBeInTheDocument();
});
it('retains failed input and retries with the same request id, then appends a reply only on click', async () => {
const bringBack = vi.fn();
api.send