合并 master:运行页入口收口并入 DirectProject 重构
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
- 保留 master 的 DirectProject / ProjectChat 重构与测试重组:原 appSurface 的 project-commands / project-preview / supervisor-runtime 三个 suite 已被 master 删除,接受删除并把相关断言重写到 tests/previewActivation.test.tsx 等新用例 - 本分支改动重放到 master 结构:运行与预览成功的反馈改走 onRunNotice toast(带 tone),对话区不再写过程提示 - 运行页顶栏统一:预览地址改成「在浏览器打开」按钮,版本入口搬进同一动作区复用同一套按钮皮,状态行与预览地址小字退役 - 生成任务入口、面板与锚点不在运行页渲染,placement 里的 run 档一并删除 - 手工合并 App.tsx / WorkspaceLauncher.tsx / model.ts / check-config.mjs / decision-log / pitfalls,并把两条决策记录与一条排障记录补进 master 版本文档
This commit is contained in:
@@ -1267,7 +1267,10 @@ function assertCheckSpaceOnlyUsesFormatSpecificRequirement() {
|
||||
args: {},
|
||||
env: { GENARRATIVE_DATABASE_BACKUP_FILES_MIN_FREE_BYTES: '1G' },
|
||||
});
|
||||
if (archive.requiredFreeBytes !== 1024n ** 3n || files.requiredFreeBytes !== 1024n ** 3n) {
|
||||
if (
|
||||
archive.requiredFreeBytes !== 1024n ** 3n ||
|
||||
files.requiredFreeBytes !== 1024n ** 3n
|
||||
) {
|
||||
failures.push(
|
||||
`空间口径覆盖参数应生效:archive=${archive.requiredFreeBytes} files=${files.requiredFreeBytes}`,
|
||||
);
|
||||
@@ -2585,7 +2588,11 @@ function assertMinimalPlanKeepsOnlyRetainedSnapshotAndTrailingCommitlog() {
|
||||
writeFileSync(path.join(clogDir, `${padded}.stdb.log`), 'log');
|
||||
writeFileSync(path.join(clogDir, `${padded}.stdb.ofs`), 'ofs');
|
||||
}
|
||||
for (const relativeDir of ['config', 'data/control-db', 'data/program-bytes']) {
|
||||
for (const relativeDir of [
|
||||
'config',
|
||||
'data/control-db',
|
||||
'data/program-bytes',
|
||||
]) {
|
||||
const directory = path.join(fixture.dataDir, relativeDir);
|
||||
mkdirSync(directory, { recursive: true });
|
||||
writeFileSync(path.join(directory, 'state.bin'), 'state');
|
||||
|
||||
@@ -26,18 +26,6 @@ const aiGameCreatorShellAppSource = fs.readFileSync(
|
||||
'apps/ai-game-creator-shell/src/App.tsx',
|
||||
'utf8',
|
||||
);
|
||||
const aiGameCreatorShellAppModelSource = fs.readFileSync(
|
||||
'apps/ai-game-creator-shell/src/features/app-shell/model.ts',
|
||||
'utf8',
|
||||
);
|
||||
const aiGameCreatorShellProjectWorkspaceChatPaneSource = fs.readFileSync(
|
||||
'apps/ai-game-creator-shell/src/features/project-workspace/ProjectWorkspaceChatPane.tsx',
|
||||
'utf8',
|
||||
);
|
||||
const aiGameCreatorShellDeveloperProjectPanelsSource = fs.readFileSync(
|
||||
'apps/ai-game-creator-shell/src/features/project-workspace/DeveloperProjectPanels.tsx',
|
||||
'utf8',
|
||||
);
|
||||
const aiGameCreatorLocalGamePreviewFrameSource = fs.readFileSync(
|
||||
'apps/ai-game-creator-shell/src/features/project-workspace/LocalGamePreviewFrame.tsx',
|
||||
'utf8',
|
||||
@@ -2623,71 +2611,7 @@ function assertAiGameCreatorShellUserDevBoundary() {
|
||||
);
|
||||
}
|
||||
|
||||
for (const snippet of [
|
||||
'function isDeveloperMode()',
|
||||
'if (!import.meta.env.DEV)',
|
||||
"return params.has('dev') || window.location.hash === '#dev';",
|
||||
]) {
|
||||
if (!sourceIncludesSnippet(aiGameCreatorShellAppModelSource, snippet)) {
|
||||
throw new Error(
|
||||
`AI game creator developer mode boundary drifted: missing ${snippet}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
for (const snippet of [
|
||||
'projectSupervisorOnly ? false : isDeveloperMode()',
|
||||
'{devMode ? (',
|
||||
'className="developer-pane"',
|
||||
]) {
|
||||
if (!sourceIncludesSnippet(aiGameCreatorShellAppSource, snippet)) {
|
||||
throw new Error(
|
||||
`AI game creator user/dev UI boundary drifted: missing ${snippet}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (
|
||||
!aiGameCreatorShellAppSource.includes('<ProjectWorkspaceChatPane') ||
|
||||
!aiGameCreatorShellProjectWorkspaceChatPaneSource.includes(
|
||||
'className="chat-pane"',
|
||||
)
|
||||
) {
|
||||
throw new Error('AI game creator user chat pane boundary drifted');
|
||||
}
|
||||
|
||||
const developerProjectPanelIndexes = [
|
||||
...aiGameCreatorShellAppSource.matchAll(/<DeveloperProjectPanels\b/g),
|
||||
].map((match) => match.index ?? -1);
|
||||
const previewFrameCount = [
|
||||
...aiGameCreatorShellDeveloperProjectPanelsSource.matchAll(/<iframe\b/g),
|
||||
].length;
|
||||
const devModeBranchIndex =
|
||||
aiGameCreatorShellAppSource.indexOf('{devMode ? (');
|
||||
const developerPaneIndex = aiGameCreatorShellAppSource.indexOf(
|
||||
'className="developer-pane"',
|
||||
);
|
||||
if (previewFrameCount !== 1) {
|
||||
throw new Error(
|
||||
'AI game creator developer pane preview frame count drifted',
|
||||
);
|
||||
}
|
||||
if (developerProjectPanelIndexes.length !== 1) {
|
||||
throw new Error(
|
||||
'AI game creator developer project panels mount count drifted',
|
||||
);
|
||||
}
|
||||
if (
|
||||
devModeBranchIndex < 0 ||
|
||||
developerPaneIndex < 0 ||
|
||||
developerProjectPanelIndexes.some(
|
||||
(index) => index < devModeBranchIndex || index < developerPaneIndex,
|
||||
)
|
||||
) {
|
||||
throw new Error(
|
||||
'AI game creator preview panels must stay inside the dev-only pane',
|
||||
);
|
||||
}
|
||||
// 上面几条只约束 DeveloperProjectPanels 自身的 iframe 数量和挂载位置,管不到 App.tsx
|
||||
// 直接内嵌 iframe 的情况——预览必须一律委托给客户端工作台,外壳自己不持有预览框。
|
||||
// 预览必须一律委托给客户端工作台,外壳自己不持有预览框。
|
||||
if ([...aiGameCreatorShellAppSource.matchAll(/<iframe\b/g)].length !== 0) {
|
||||
throw new Error(
|
||||
'AI game creator app shell must delegate preview iframe to the client workbench',
|
||||
@@ -2781,19 +2705,6 @@ function assertAiGameCreatorShellUserDevBoundary() {
|
||||
'AI game creator normal startup must not automatically open a developer window',
|
||||
);
|
||||
}
|
||||
for (const snippet of [
|
||||
'fn open_project_supervisor_chat_window(',
|
||||
'#[cfg(not(debug_assertions))]',
|
||||
'项目总控对话窗口仅在开发构建中可用',
|
||||
'index.html?supervisor-chat&projectPath=',
|
||||
]) {
|
||||
if (!sourceIncludesSnippet(aiGameCreatorShellTauriSource, snippet)) {
|
||||
throw new Error(
|
||||
`AI game creator supervisor chat window must stay developer-only: ${snippet}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const workspaceWindowCommandIndex = aiGameCreatorShellTauriSource.indexOf(
|
||||
'fn open_game_creator_workspace_window(',
|
||||
);
|
||||
|
||||
@@ -1106,7 +1106,9 @@ function calculateFilesRequiredFreeBytes({ dataSizeBytes, env }) {
|
||||
const ratioBasisPoints = BigInt(Math.ceil(ratio * 10000));
|
||||
const ratioRequirement = (dataSizeBytes * ratioBasisPoints + 9999n) / 10000n;
|
||||
const floorRequirement = BigInt(DEFAULT_FILES_EXTRA_FREE_BYTES);
|
||||
return ratioRequirement > floorRequirement ? ratioRequirement : floorRequirement;
|
||||
return ratioRequirement > floorRequirement
|
||||
? ratioRequirement
|
||||
: floorRequirement;
|
||||
}
|
||||
|
||||
export function describeBackupSpaceRequirement({
|
||||
@@ -1126,7 +1128,13 @@ export function describeBackupSpaceRequirement({
|
||||
return { storageFormat, dataSizeBytes, availableBytes, requiredFreeBytes };
|
||||
}
|
||||
|
||||
function assertSufficientWorkDirSpace({ dataDir, workDir, args, env, storageFormat = 'archive' }) {
|
||||
function assertSufficientWorkDirSpace({
|
||||
dataDir,
|
||||
workDir,
|
||||
args,
|
||||
env,
|
||||
storageFormat = 'archive',
|
||||
}) {
|
||||
const { dataSizeBytes, availableBytes, requiredFreeBytes } =
|
||||
describeBackupSpaceRequirement({
|
||||
dataDir,
|
||||
@@ -4468,7 +4476,9 @@ async function main() {
|
||||
throw new Error('--minimal 只支持 --storage-format files。');
|
||||
}
|
||||
if (args.minimal && args.mode === 'history') {
|
||||
throw new Error('--minimal 只支持 --mode full:minimal 自身就是自包含快照。');
|
||||
throw new Error(
|
||||
'--minimal 只支持 --mode full:minimal 自身就是自包含快照。',
|
||||
);
|
||||
}
|
||||
|
||||
if (args.minimal && args.freezeDir) {
|
||||
@@ -4747,7 +4757,13 @@ async function main() {
|
||||
const restartServicesAfter = collectRestartServicesAfterBackup({ args, env });
|
||||
const stopMarkerPath = databaseBackupStopMarkerPath(workDir);
|
||||
try {
|
||||
assertSufficientWorkDirSpace({ dataDir, workDir, args, env, storageFormat: 'archive' });
|
||||
assertSufficientWorkDirSpace({
|
||||
dataDir,
|
||||
workDir,
|
||||
args,
|
||||
env,
|
||||
storageFormat: 'archive',
|
||||
});
|
||||
serviceStopped = stopServiceIfNeeded(stopService, stopMarkerPath);
|
||||
archivePath = createArchive({ dataDir, workDir, fileName });
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user