移除错误报告手动入口

删除 /bug-report 聊天命令与崩溃页报告按钮

同步项目摘要文案和 AGC 错误报告方案
This commit is contained in:
2026-09-01 19:04:24 +08:00
parent 2aafcf24fa
commit 5315a3e338
11 changed files with 25 additions and 86 deletions
@@ -3,14 +3,13 @@ import {
type ErrorInfo,
type FormEvent,
type ReactNode,
useCallback,
useEffect,
useState,
} from 'react';
import type { AuthUser } from '../../../../packages/shared/src/contracts/auth';
import brandIcon from '../../../../packages/shared/src/icons/taonier-product-ip.png';
import { ErrorReportDialog } from '../components/error-report/ErrorReportDialog';
import { ErrorReportCenter } from '../components/error-report/ErrorReportDialog';
import {
clearStoredAuthAccessToken,
getClientAuthErrorMessage,
@@ -34,7 +33,7 @@ import {
import {
captureClientError,
installWebviewLogBridge,
OPEN_ERROR_REPORT_EVENT,
shouldCaptureClientError,
} from '../services/errorReporting';
import {
beginPlatformSessionClearTransition,
@@ -50,7 +49,6 @@ import {
type ClientRuntimeErrorBoundaryProps = {
children: ReactNode;
onLogout: () => void;
onReport?: () => void;
};
type ClientRuntimeErrorBoundaryState = {
@@ -95,11 +93,6 @@ export class ClientRuntimeErrorBoundary extends Component<
<button type="button" onClick={this.props.onLogout}>
</button>
{this.props.onReport ? (
<button type="button" onClick={this.props.onReport}>
</button>
) : null}
</section>
</main>
);
@@ -123,7 +116,6 @@ export function AuthenticatedClient({
const [loginBusy, setLoginBusy] = useState(false);
const [codeBusy, setCodeBusy] = useState(false);
const [codeCooldownSeconds, setCodeCooldownSeconds] = useState(0);
const [errorReportOpen, setErrorReportOpen] = useState(false);
const initialServerSelection = getClientServerSelection();
const [serverSelection, setServerSelection] = useState<ClientServerSelection>(
initialServerSelection,
@@ -131,13 +123,6 @@ export function AuthenticatedClient({
const [customServerUrl, setCustomServerUrl] = useState(
initialServerSelection.customBaseUrl,
);
const openManualErrorReport = useCallback(async () => {
await captureClientError(new Error('用户主动报告问题'), {
source: 'manual',
});
setErrorReportOpen(true);
}, []);
useEffect(() => {
const uninstallWebviewLogBridge = installWebviewLogBridge();
const handleError = (event: ErrorEvent) => {
@@ -157,15 +142,6 @@ export function AuthenticatedClient({
};
}, []);
useEffect(() => {
window.addEventListener(OPEN_ERROR_REPORT_EVENT, openManualErrorReport);
return () =>
window.removeEventListener(
OPEN_ERROR_REPORT_EVENT,
openManualErrorReport,
);
}, [openManualErrorReport]);
function persistServerSelection() {
try {
const next = setClientServerSelection({
@@ -357,10 +333,11 @@ export function AuthenticatedClient({
setCodeCooldownSeconds(Math.max(0, Math.floor(response.cooldownSeconds)));
setLoginStatus(`验证码已发送,${response.expiresInSeconds} 秒内有效`);
} catch (error) {
void captureClientError(error, {
source: 'auth',
action: 'send-login-code',
});
if (shouldCaptureClientError(error))
void captureClientError(error, {
source: 'auth',
action: 'send-login-code',
});
setLoginStatus(error instanceof Error ? error.message : String(error));
} finally {
setCodeBusy(false);
@@ -419,7 +396,8 @@ export function AuthenticatedClient({
setCode('');
setPassword('');
} catch (error) {
void captureClientError(error, { source: 'auth', action: 'login' });
if (shouldCaptureClientError(error))
void captureClientError(error, { source: 'auth', action: 'login' });
setLoginStatus(error instanceof Error ? error.message : String(error));
} finally {
setLoginBusy(false);
@@ -589,16 +567,10 @@ export function AuthenticatedClient({
return (
<>
<ClientRuntimeErrorBoundary
onLogout={logout}
onReport={() => void openManualErrorReport()}
>
<ClientRuntimeErrorBoundary onLogout={logout}>
{children({ user: authUser, logout })}
</ClientRuntimeErrorBoundary>
<ErrorReportDialog
open={errorReportOpen}
onClose={() => setErrorReportOpen(false)}
/>
<ErrorReportCenter />
</>
);
}
@@ -159,7 +159,7 @@ export function summarizeProjectKnownIssues(
};预览 ${previewSummary}`,
`- 已知问题:${knownIssueSummary}`,
'- 试玩限制:本地 Web 原型;小范围 5-10 分钟试玩;不承诺账号、云存档、排行榜、付费或长期兼容',
'- 反馈入口:单个问题走 /bug-report;整体体验走 /feedback;版本变化走 /release-notes',
'- 反馈入口:客户端问题由自动诊断提示;整体体验走 /feedback;版本变化走 /release-notes',
'- 发送前检查:可试玩状态先 /run;交付口径看 /share;对外资料看 /media-kit',
'- 边界:只准备已知问题清单;不读取文件;不启动或打开预览;不导出试玩包;不上传云端;不发布作品;不写项目',
`- 建议:${draftCommand}`,
@@ -75,7 +75,6 @@ export function summarizeNextProjectActions(
addSuggestion('查看隐私与导出边界', '/privacy');
addSuggestion('查看首批试玩对象', '/audience');
addSuggestion('准备试玩邀请文案', '/invite');
addSuggestion('报告问题', '/bug-report');
addSuggestion('准备试玩问卷问题', '/survey');
addSuggestion('准备封面与缩略图检查', '/cover');
addSuggestion('准备宣传截图清单', '/screenshots');
@@ -290,8 +290,8 @@ export function summarizeProjectPlaytestSurvey(
'- 使用场景:发给首批 3-5 位测试者;每人 5-10 分钟;先自由玩一局再回答',
'- 问题清单:1. 30 秒内你觉得目标是什么?2. 第一次操作哪里最卡?3. 胜负/重开是否清楚?4. 难度/节奏感觉如何?5. 最想保留和最想改的各一项?',
'- 记录格式:每题 1-5 分 + 一句话;补充设备、输入方式、是否愿意再玩一局',
'- 追踪方式:单个问题走 /bug-report;整体反馈走 /feedback;下一轮改动走 /revise',
'- 参考:/invite/audience/feedback/bug-report',
'- 追踪方式:客户端问题由自动诊断提示;整体反馈走 /feedback;下一轮改动走 /revise',
'- 参考:/invite/audience/feedback',
'- 边界:只准备试玩问卷;不读取文件;不启动或打开预览;不导出试玩包;不写项目',
`- 建议:${draftCommand}`,
].join('\n'),
@@ -557,7 +557,7 @@ export function summarizeProjectPlaytestFaq(
};预览 ${previewSummary}`,
'- 问答清单:1. 这是什么?2. 怎么开始和重开?3. 需要反馈什么?4. 打不开或卡住怎么办?5. 能不能转发或公开?',
'- 回答口径:早期本地 Web 原型;5-10 分钟试玩;重点反馈目标理解、操作手感、难度、bug 和还想不想再玩',
'- 测试者提醒:先自由玩一局;不要评价完成度;问卷走 /survey;单个问题走 /bug-report',
'- 测试者提醒:先自由玩一局;不要评价完成度;问卷走 /survey;客户端问题由自动诊断提示',
'- 交付搭配:/invite/share/screenshots/trailer',
'- 边界:只准备试玩常见问答;不读取文件;不启动或打开预览;不导出试玩包;不写项目',
`- 建议:${draftCommand}`,
@@ -624,7 +624,7 @@ export function summarizeProjectCommunityPost(
`- 短文案:我做了一个早期 Web 小游戏原型《${nextManifest.name}》,核心目标是${goal}。想找 3-5 位朋友试玩 5 分钟,重点看能不能理解目标、操作顺不顺、还想不想再来一局。`,
'- 长文案结构:一句玩法目标;一张截图或短视频;试玩方式;希望收到的三类反馈;已知限制',
'- 标签建议:#Web小游戏 #原型试玩 #AI游戏创作 #本地试玩',
'- CTA:愿意试玩请回复;遇到问题按 /faq 或 /bug-report 的口径反馈',
'- CTA:愿意试玩请回复;遇到问题按 /faq 的口径反馈,客户端问题会自动提示',
'- 参考:/faq/screenshots/trailer/store/share',
'- 边界:只准备社区发布文案;不上传云端;不发布作品;不读取文件;不启动或打开预览;不导出试玩包;不写项目',
`- 建议:${draftCommand}`,
@@ -290,7 +290,7 @@ export function summarizeProjectCompatibilityNotes(
`- 输入兼容:${inputSummary}`,
'- 推荐环境:桌面 Chrome / Edge 最新版;本机 127.0.0.1 预览;移动浏览器只做早期体验',
'- 不承诺:旧浏览器、低端设备、离线模式、云存档、账号同步、手柄或多端数据一致',
'- 反馈口径:设备 / 浏览器 / 输入方式 / 截图或录屏;问题记录走 /bug-report',
'- 反馈口径:设备 / 浏览器 / 输入方式 / 截图或录屏;客户端检测到问题时会自动提示',
'- 参考:/mobile/accessibility/performance/known-issues',
'- 边界:只准备兼容性说明;不读取文件;不启动或打开预览;不导出试玩包;不上传云端;不发布作品;不写项目',
`- 建议:${draftCommand}`,
@@ -123,7 +123,6 @@ export const chatCommandHelp = [
'/privacy:查看隐私与导出边界',
'/audience:查看首批试玩对象',
'/invite:准备试玩邀请文案',
'/bug-report:报告问题',
'/survey:准备试玩问卷问题',
'/cover:准备封面与缩略图检查',
'/screenshots:准备宣传截图清单',
@@ -9,7 +9,6 @@ import type {
AgentStatusCard,
ChatMessage,
} from '../../app/types';
import { OPEN_ERROR_REPORT_EVENT } from '../../services/errorReporting';
import {
summarizeAgentReviewState,
summarizeAgentRunBudget,
@@ -506,20 +505,6 @@ export function handleProjectSummaryChatCommand({
return true;
}
if (prompt === '/bug-report') {
setMessages((current) => [
...current,
{
role: 'assistant',
text: '已打开“报告问题”面板,请填写遇到的问题。',
},
]);
if (typeof window !== 'undefined') {
window.dispatchEvent(new CustomEvent(OPEN_ERROR_REPORT_EVENT));
}
return true;
}
if (prompt === '/survey') {
if (!requireChatProjectForUserAction()) {
return true;
@@ -5072,7 +5072,6 @@ export function registerDeveloperToolsTests() {
expect(screen.getByText(/\/privacy:查看隐私与导出边界/)).not.toBeNull();
expect(screen.getByText(/\/audience:查看首批试玩对象/)).not.toBeNull();
expect(screen.getByText(/\/invite:准备试玩邀请文案/)).not.toBeNull();
expect(screen.getByText(/\/bug-report:报告问题/)).not.toBeNull();
expect(screen.getByText(/\/survey:准备试玩问卷问题/)).not.toBeNull();
expect(screen.getByText(/\/cover:准备封面与缩略图检查/)).not.toBeNull();
expect(screen.getByText(/\/screenshots:准备宣传截图清单/)).not.toBeNull();
@@ -1,4 +1,3 @@
import { OPEN_ERROR_REPORT_EVENT } from '../../../../src/services/errorReporting';
import { expect, fireEvent, screen, submitChat, within } from '../../harness';
import type { PreviewShortcutInvoke } from './invoke-mock';
@@ -47,7 +46,6 @@ export async function assertPlaytestAndReleaseShortcutFlow(
expect(screen.getByText(/查看隐私与导出边界:\/privacy/)).not.toBeNull();
expect(screen.getByText(/查看首批试玩对象:\/audience/)).not.toBeNull();
expect(screen.getByText(/准备试玩邀请文案:\/invite/)).not.toBeNull();
expect(screen.getByText(/报告问题:\/bug-report/)).not.toBeNull();
expect(screen.getByText(/准备试玩问卷问题:\/survey/)).not.toBeNull();
expect(screen.getByText(/准备封面与缩略图检查:\/cover/)).not.toBeNull();
expect(screen.getByText(/准备宣传截图清单:\/screenshots/)).not.toBeNull();
@@ -648,20 +646,6 @@ export async function assertPlaytestAndReleaseShortcutFlow(
([command]) => command === 'control_agent_run',
).length,
};
let openedErrorReport = false;
const onErrorReportOpen = () => {
openedErrorReport = true;
};
window.addEventListener(OPEN_ERROR_REPORT_EVENT, onErrorReportOpen);
try {
submitChat('/bug-report');
expect(
await screen.findByText('已打开“报告问题”面板,请填写遇到的问题。'),
).not.toBeNull();
expect(openedErrorReport).toBe(true);
} finally {
window.removeEventListener(OPEN_ERROR_REPORT_EVENT, onErrorReportOpen);
}
expect(
invoke.mock.calls.filter(
([command]) => command === 'get_local_game_manifest',
@@ -753,10 +737,10 @@ export async function assertPlaytestAndReleaseShortcutFlow(
'记录格式:每题 1-5 分 + 一句话;补充设备、输入方式、是否愿意再玩一局',
);
expect(surveyMessage.textContent).toContain(
'追踪方式:单个问题走 /bug-report;整体反馈走 /feedback;下一轮改动走 /revise',
'追踪方式:客户端问题由自动诊断提示;整体反馈走 /feedback;下一轮改动走 /revise',
);
expect(surveyMessage.textContent).toContain(
'参考:/invite/audience/feedback/bug-report',
'参考:/invite/audience/feedback',
);
expect(surveyMessage.textContent).toContain(
'边界:只准备试玩问卷;不读取文件;不启动或打开预览;不导出试玩包;不写项目',
@@ -1185,7 +1169,7 @@ export async function assertPlaytestAndReleaseShortcutFlow(
'回答口径:早期本地 Web 原型;5-10 分钟试玩;重点反馈目标理解、操作手感、难度、bug 和还想不想再玩',
);
expect(faqMessage.textContent).toContain(
'测试者提醒:先自由玩一局;不要评价完成度;问卷走 /survey;单个问题走 /bug-report',
'测试者提醒:先自由玩一局;不要评价完成度;问卷走 /survey;客户端问题由自动诊断提示',
);
expect(faqMessage.textContent).toContain(
'交付搭配:/invite/share/screenshots/trailer',
@@ -1296,7 +1280,7 @@ export async function assertPlaytestAndReleaseShortcutFlow(
'标签建议:#Web小游戏 #原型试玩 #AI游戏创作 #本地试玩',
);
expect(postMessage.textContent).toContain(
'CTA:愿意试玩请回复;遇到问题按 /faq 或 /bug-report 的口径反馈',
'CTA:愿意试玩请回复;遇到问题按 /faq 的口径反馈,客户端问题会自动提示',
);
expect(postMessage.textContent).toContain(
'参考:/faq/screenshots/trailer/store/share',
@@ -1735,7 +1719,7 @@ export async function assertPlaytestAndReleaseShortcutFlow(
'试玩限制:本地 Web 原型;小范围 5-10 分钟试玩;不承诺账号、云存档、排行榜、付费或长期兼容',
);
expect(knownIssueMessage.textContent).toContain(
'反馈入口:单个问题走 /bug-report;整体体验走 /feedback;版本变化走 /release-notes',
'反馈入口:客户端问题由自动诊断提示;整体体验走 /feedback;版本变化走 /release-notes',
);
expect(knownIssueMessage.textContent).toContain(
'发送前检查:可试玩状态先 /run;交付口径看 /share;对外资料看 /media-kit',
@@ -916,7 +916,7 @@ export async function assertProjectAndDesignShortcutFlow(
'不承诺:旧浏览器、低端设备、离线模式、云存档、账号同步、手柄或多端数据一致',
);
expect(compatibilityMessage.textContent).toContain(
'反馈口径:设备 / 浏览器 / 输入方式 / 截图或录屏;问题记录走 /bug-report',
'反馈口径:设备 / 浏览器 / 输入方式 / 截图或录屏;客户端检测到问题时会自动提示',
);
expect(compatibilityMessage.textContent).toContain(
'参考:/mobile/accessibility/performance/known-issues',
@@ -11,7 +11,8 @@ AI Game Creator Shell 采用 IDEA 风格的当前进程错误报告:错误事
- 指纹计算可使用调用方的 page/action 及脱敏后的首个调用点作为进程内区分输入,但这些上下文不会作为事件字段上传;消息与 stack 在入池前统一脱敏,WebCrypto 失败时降级为稳定可读指纹,采集本身不得产生新的未处理拒绝。
- 客户端 API 自动采集只覆盖网络错误、408 和 5xx;预期的 4xx 登录/鉴权失败不进入错误报告池。
- Rust 侧通过 `app_log!` 将普通文本日志同时输出到 stderr 和 AppData `diagnostics/application.log`,超出 256 KiB 滚动到 `application.previous.log`WebView 的 console 输出通过 `append_application_log` 镜像到同一 raw log,并在客户端桥接处再次脱敏;`read_diagnostic_logs` 只读取应用级日志。
- 报告面板允许填写最多 2,000 字中文描述并取消日志附件;本版本不支持截图或任意文件附件。
- 报告面板只由自动诊断通知中的“查看并报告”打开,不提供聊天命令、崩溃页按钮或其他手动入口;默认选中最新事件,其他事件可勾选。允许填写最多 2,000 字中文描述并取消日志附件;本版本不支持截图或任意文件附件。
- 错误事件先在当前进程内存池按 fingerprint 合并,经过 5 秒聚合后只显示一条非阻塞通知;通知支持“查看并报告”和“忽略”,同一 fingerprint 在本次运行中只提醒一次。通知不直接打开阻塞式报告面板。
- 上传失败只在当前进程显示失败并允许用户再次提交,不跨重启恢复事件池,不后台自动重试。
## HTTP 与存储