修复模块脚本视觉资产门
复用有界模块投影检查 inline 与 external module 的可达 drawImage 拒绝未链接、恒假与纯 HTML 路径视觉诱饵 补充完成合同回归和视觉门文档
This commit is contained in:
+61
-45
@@ -480,6 +480,7 @@ fn autonomous_code_prototype_art_asset_reference_gap_at(
|
||||
}
|
||||
} else if !matches!(game_index_missing_visible_art_slice(root, &html), Ok(None))
|
||||
&& !game_index_visibly_uses_visual_asset(
|
||||
root,
|
||||
&html,
|
||||
asset_path,
|
||||
asset_dimensions,
|
||||
@@ -507,6 +508,7 @@ fn game_index_missing_visible_art_slice(
|
||||
let slice_path = slice.path;
|
||||
let dimensions = (slice.width, slice.height);
|
||||
if !game_index_visibly_uses_visual_asset(
|
||||
root,
|
||||
html,
|
||||
&slice_path,
|
||||
dimensions,
|
||||
@@ -2814,6 +2816,7 @@ enum VisualAssetUsageRequirement {
|
||||
}
|
||||
|
||||
fn game_index_visibly_uses_visual_asset(
|
||||
root: &Path,
|
||||
html: &[u8],
|
||||
asset_path: &str,
|
||||
asset_dimensions: (u32, u32),
|
||||
@@ -2825,9 +2828,6 @@ fn game_index_visibly_uses_visual_asset(
|
||||
};
|
||||
let original_content = strip_art_reference_comments(html);
|
||||
let heuristic_content = original_content.to_ascii_lowercase();
|
||||
if !heuristic_content.contains(&asset_path) {
|
||||
return false;
|
||||
}
|
||||
let markup = strip_script_blocks(&heuristic_content);
|
||||
|
||||
let mut tag_cursor = 0;
|
||||
@@ -2893,56 +2893,68 @@ fn game_index_visibly_uses_visual_asset(
|
||||
}) else {
|
||||
return false;
|
||||
};
|
||||
let identifiers = canvas_visual_identifiers(&heuristic_content, &markup, &asset_path);
|
||||
if identifiers.is_empty() {
|
||||
let Ok(external_javascript) = read_external_gameplay_javascript_at(root, &original_content)
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
let mut classic_global = executable_javascript_from_html(&original_content);
|
||||
classic_global.push_str(&external_javascript.classic_global);
|
||||
if !javascript_is_syntactically_valid(&classic_global, false) {
|
||||
classic_global.clear();
|
||||
}
|
||||
let original_javascript = executable_javascript_from_html(&original_content);
|
||||
let heuristic_javascript = original_javascript.to_ascii_lowercase();
|
||||
let mut significant_draws = 0usize;
|
||||
let function_ranges = named_javascript_function_ranges(&original_javascript);
|
||||
let mut draw_cursor = 0;
|
||||
while let Some(offset) = heuristic_javascript[draw_cursor..].find("drawimage(") {
|
||||
let call = draw_cursor + offset;
|
||||
let arguments_start = call + "drawimage(".len();
|
||||
draw_cursor = arguments_start;
|
||||
if position_is_inside_javascript_string(&heuristic_javascript, call)
|
||||
|| !javascript_position_is_reachable(&original_javascript, &function_ranges, call)
|
||||
{
|
||||
continue;
|
||||
let mut javascript_units = vec![classic_global];
|
||||
javascript_units.extend(external_javascript.module_units);
|
||||
javascript_units.into_iter().any(|original_javascript| {
|
||||
let heuristic_javascript = original_javascript.to_ascii_lowercase();
|
||||
let identifiers = canvas_visual_identifiers(&heuristic_javascript, &markup, &asset_path);
|
||||
if identifiers.is_empty() {
|
||||
return false;
|
||||
}
|
||||
let Some(arguments_end) =
|
||||
javascript_call_arguments_end(&heuristic_javascript, arguments_start)
|
||||
else {
|
||||
break;
|
||||
};
|
||||
let arguments =
|
||||
split_javascript_arguments(&heuristic_javascript[arguments_start..arguments_end]);
|
||||
if arguments
|
||||
.first()
|
||||
.is_some_and(|identifier| identifiers.contains(*identifier))
|
||||
{
|
||||
let (significant, _, _) = draw_image_metrics(&arguments, asset_dimensions);
|
||||
significant_draws += usize::from(significant);
|
||||
if requirement == VisualAssetUsageRequirement::AnyVisible && significant {
|
||||
return true;
|
||||
}
|
||||
if requirement == VisualAssetUsageRequirement::CanvasDraw
|
||||
&& matches!(arguments.len(), 5 | 9)
|
||||
&& draw_image_has_visible_destination(&arguments, canvas_dimensions)
|
||||
let mut significant_draws = 0usize;
|
||||
let function_ranges = named_javascript_function_ranges(&original_javascript);
|
||||
let mut draw_cursor = 0;
|
||||
while let Some(offset) = heuristic_javascript[draw_cursor..].find("drawimage(") {
|
||||
let call = draw_cursor + offset;
|
||||
let arguments_start = call + "drawimage(".len();
|
||||
draw_cursor = arguments_start;
|
||||
if position_is_inside_javascript_string(&heuristic_javascript, call)
|
||||
|| !javascript_position_is_reachable(&original_javascript, &function_ranges, call)
|
||||
{
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
if requirement == VisualAssetUsageRequirement::AtlasCanvasCrop
|
||||
&& arguments.len() == 9
|
||||
&& draw_image_has_visible_destination(&arguments, canvas_dimensions)
|
||||
let Some(arguments_end) =
|
||||
javascript_call_arguments_end(&heuristic_javascript, arguments_start)
|
||||
else {
|
||||
break;
|
||||
};
|
||||
let arguments =
|
||||
split_javascript_arguments(&heuristic_javascript[arguments_start..arguments_end]);
|
||||
if arguments
|
||||
.first()
|
||||
.is_some_and(|identifier| identifiers.contains(*identifier))
|
||||
{
|
||||
return true;
|
||||
let (significant, _, _) = draw_image_metrics(&arguments, asset_dimensions);
|
||||
significant_draws += usize::from(significant);
|
||||
if requirement == VisualAssetUsageRequirement::AnyVisible && significant {
|
||||
return true;
|
||||
}
|
||||
if requirement == VisualAssetUsageRequirement::CanvasDraw
|
||||
&& matches!(arguments.len(), 5 | 9)
|
||||
&& draw_image_has_visible_destination(&arguments, canvas_dimensions)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if requirement == VisualAssetUsageRequirement::AtlasCanvasCrop
|
||||
&& arguments.len() == 9
|
||||
&& draw_image_has_visible_destination(&arguments, canvas_dimensions)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
draw_cursor = arguments_end + 1;
|
||||
}
|
||||
draw_cursor = arguments_end + 1;
|
||||
}
|
||||
requirement == VisualAssetUsageRequirement::AnyVisible && significant_draws > 0
|
||||
requirement == VisualAssetUsageRequirement::AnyVisible && significant_draws > 0
|
||||
})
|
||||
}
|
||||
|
||||
fn dynamic_canvas_coordinate_is_bounded(value: &str, axis_extent: &str) -> bool {
|
||||
@@ -10286,7 +10298,9 @@ mod visible_destination_tests {
|
||||
|
||||
#[test]
|
||||
fn visual_asset_reachability_keeps_original_identifier_case_for_called_main_loop() {
|
||||
let root = tempfile::tempdir().expect("create visual reachability fixture");
|
||||
assert!(game_index_visibly_uses_visual_asset(
|
||||
root.path(),
|
||||
&mixed_case_main_loop_html("MainLoop"),
|
||||
"assets/art-spec.png",
|
||||
(64, 64),
|
||||
@@ -10296,7 +10310,9 @@ mod visible_destination_tests {
|
||||
|
||||
#[test]
|
||||
fn visual_asset_reachability_does_not_merge_differently_cased_main_loops() {
|
||||
let root = tempfile::tempdir().expect("create visual reachability fixture");
|
||||
assert!(!game_index_visibly_uses_visual_asset(
|
||||
root.path(),
|
||||
&mixed_case_main_loop_html("mainloop"),
|
||||
"assets/art-spec.png",
|
||||
(64, 64),
|
||||
|
||||
+70
@@ -4969,6 +4969,76 @@ fn cli_code_prototype_keeps_registered_canvas_spritesheet_gate_when_editor_is_co
|
||||
assert!(autonomous_game_build_completion_blocker_at_locked(&root, &code_state).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cli_code_prototype_accepts_linked_inline_and_external_modules_for_canvas_atlas_use() {
|
||||
let _config_guard = crate::tests::write_test_local_config(
|
||||
r#"{"editorApi":{"apiKey":"cli-module-art-gate-key"}}"#.to_string(),
|
||||
);
|
||||
let (_temporary, root, parent_state, _contract) =
|
||||
autonomous_fixture("创建完整小游戏", "cli-module-art-gate-parent");
|
||||
update_manifest_task_status_at(&root, "code-prototype", GameCreationAppTaskStatus::Running)
|
||||
.expect("mark code prototype running");
|
||||
let code_record =
|
||||
queue_autonomous_manifest_child_fixture(&root, &parent_state, "code-prototype");
|
||||
let code_state = agent_runtime_state_from_task_record(&code_record);
|
||||
|
||||
advance_game_index_revision(
|
||||
&root,
|
||||
&code_state,
|
||||
"<!doctype html><html><body><canvas width=320 height=180></canvas><script type='module'>const context=document.querySelector('canvas').getContext('2d');const sheet=new Image();sheet.src='../assets/art-spritesheet.png';function renderAtlas(){context.drawImage(sheet,0,0,32,32,0,0,64,64)}renderAtlas();</script></body></html>",
|
||||
);
|
||||
assert!(
|
||||
autonomous_game_build_completion_blocker_at_locked(&root, &code_state).is_none(),
|
||||
"a reachable inline module atlas crop must satisfy the visual asset gate"
|
||||
);
|
||||
|
||||
fs::write(
|
||||
root.join("game/render.mjs"),
|
||||
"const context=document.querySelector('canvas').getContext('2d');const sheet=new Image();sheet.src='../assets/art-spritesheet.png';export function renderAtlas(){context.drawImage(sheet,0,0,32,32,0,0,64,64)}renderAtlas();",
|
||||
)
|
||||
.expect("write external visual module");
|
||||
advance_game_index_revision(
|
||||
&root,
|
||||
&code_state,
|
||||
"<!doctype html><html><body><canvas width=320 height=180></canvas><script type='module' src='./render.mjs'></script></body></html>",
|
||||
);
|
||||
assert!(
|
||||
autonomous_game_build_completion_blocker_at_locked(&root, &code_state).is_none(),
|
||||
"a reachable external module atlas crop must satisfy the visual asset gate"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cli_code_prototype_rejects_unlinked_unreachable_modules_and_html_path_decoys() {
|
||||
let _config_guard = crate::tests::write_test_local_config(
|
||||
r#"{"editorApi":{"apiKey":"cli-module-decoy-key"}}"#.to_string(),
|
||||
);
|
||||
let (_temporary, root, parent_state, _contract) =
|
||||
autonomous_fixture("创建完整小游戏", "cli-module-decoy-parent");
|
||||
update_manifest_task_status_at(&root, "code-prototype", GameCreationAppTaskStatus::Running)
|
||||
.expect("mark code prototype running");
|
||||
let code_record =
|
||||
queue_autonomous_manifest_child_fixture(&root, &parent_state, "code-prototype");
|
||||
let code_state = agent_runtime_state_from_task_record(&code_record);
|
||||
let decoy = "const context=document.querySelector('canvas').getContext('2d');const sheet=new Image();sheet.src='../assets/art-spritesheet.png';context.drawImage(sheet,0,0,32,32,0,0,64,64);";
|
||||
fs::write(root.join("game/unlinked.mjs"), decoy).expect("write unlinked visual decoy");
|
||||
fs::write(
|
||||
root.join("game/entry.mjs"),
|
||||
"if(false){import('./unreachable.mjs')} const context=document.querySelector('canvas').getContext('2d');const sheet=new Image();context.drawImage(sheet,0,0,32,32,0,0,64,64);",
|
||||
)
|
||||
.expect("write linked module entry");
|
||||
fs::write(root.join("game/unreachable.mjs"), decoy).expect("write unreachable visual module");
|
||||
advance_game_index_revision(
|
||||
&root,
|
||||
&code_state,
|
||||
"<!doctype html><html><body><p>../assets/art-spritesheet.png</p><canvas width=320 height=180></canvas><script type='module' src='./entry.mjs'></script></body></html>",
|
||||
);
|
||||
assert!(
|
||||
autonomous_game_build_completion_blocker_at_locked(&root, &code_state).is_some(),
|
||||
"an unlinked file, a literal-false dynamic module, and a plain HTML path must not combine into visual evidence"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn game_chat_code_prototype_fails_closed_without_generated_spritesheet() {
|
||||
let _config_guard = crate::tests::write_test_local_config("{}".to_string());
|
||||
|
||||
@@ -5985,7 +5985,7 @@
|
||||
- JavaScript / ESM 控制流身份补充:member alias、局部 receiver 与动态 namespace 的赋值必须保存 assignment position 和 enclosing function scope;函数体使用按真实 invocation position 选择当时事件,多次调用跨越赋值边界时合并可能 owner,恒假分支、未调用函数或调用之后的赋值不得覆盖更早使用点。导出的 function 与 class/object member 额外以模块初始化结束作为潜在外部调用点,使声明后生效的顶层赋值进入 demand,同时保留更早本地调用状态。动态依赖传播以原始 owner 模块 AST span 为权威,不因投影重排声明或省略独立赋值语句重算 alias。class method owner 进一步区分 static / instance,class expression 与实例化 alias 使用同一 owner 图。callback API 只接受 semantic 未解析的已知全局调度函数,以及可由 AST 证明的 literal array / dynamic import 调用;被用户定义或遮蔽的同名 `setTimeout / map / then` 不得推断执行参数。
|
||||
- JavaScript / ESM 深层可达性补充:constructor、`.call/.apply` 与受控 inline callback 建立真实 invocation;具名 function expression 不再生成遮蔽外层 binding 的重叠节点,普通 inline function / arrow 未被执行时保持不可达。受控 callback API 名大小写敏感,并以精确参数索引建立执行边:timer、microtask、RAF、Promise 与数组迭代取第一个参数,`addEventListener` 取第二个参数,delay、initial value、event type、options 和额外参数保持普通值。条件/循环赋值合并执行与跳过状态,conditional expression 合并各 owner,未知确定赋值显式 invalidation;已调用函数对外层 alias 的副作用按调用位置传播,`super` owner 固定在 class 定义点。恒假扫描先屏蔽 parser 识别的注释和 literal;恒假分支区间随单次函数可达性 analysis 预计算、排序合并并以借用二分索引查询,不再使用 thread-local 完整源码 key 或命中时 clone ranges。投影 canonical 根名避让两侧全部非 import binding,dynamic shorthand 保留原键,循环模块按相同原始声明去重,同名 dynamic export 不得拉入无引用本地声明。
|
||||
- JavaScript / ESM 构造、继承与 callable 补充:`new` 沿冻结的 class owner 图执行本类显式 constructor、显式 `super()` 或隐式 derived constructor,并支持直接 class expression;instance / static 成员未 override 时继续沿 `extends` 链查找。普通嵌套 function 不继承 class `this`,arrow 保持词法 owner。`let binding; binding = function/arrow`、本地 function alias 和 Function.prototype `.bind()` 结果都建立 callable identity;`.call/.apply/.bind` 只有在 receiver 可解析为 callable 时采用 Function.prototype 语义,业务对象同名方法仍作为普通 receiver method 执行。
|
||||
- JavaScript / ESM callable、callback 与视觉可达性补充:callable conditional expression 合并两端全部身份;声明式与后续赋值式 member `.bind()` 都冻结成员 owner。受控 callback API 穿透 callee / receiver 外层括号,Promise `.then` 取 fulfilled 与 rejected 两个 callback 槽,`.catch/.finally` 仍仅取首槽。视觉资产路径与 `drawImage` 启发式可使用 ASCII 小写副本,但 AST、semantic binding 和函数可达性只解析原始大小写 JavaScript,大小写不同的 `MainLoop/mainloop` 不得合并。
|
||||
- JavaScript / ESM callable、callback 与视觉可达性补充:callable conditional expression 合并两端全部身份;声明式与后续赋值式 member `.bind()` 都冻结成员 owner。受控 callback API 穿透 callee / receiver 外层括号,Promise `.then` 取 fulfilled 与 rejected 两个 callback 槽,`.catch/.finally` 仍仅取首槽。视觉资产路径与 `drawImage` 启发式可使用 ASCII 小写副本,但 AST、semantic binding 和函数可达性只解析原始大小写 JavaScript,大小写不同的 `MainLoop/mainloop` 不得合并。Canvas 视觉门复用受 256 文件、累计源码 `2 MiB` 与投影处理 `32 MiB` 限制的 inline / external module 链接与投影结果,逐 unit 关联路径、图片变量和可达绘制;inline `type=module`、本地 `src` module 及真实可达依赖可作证据,未链接文件、恒假动态依赖、未调用函数、跨 unit 拼接与纯 HTML 路径诱饵均失败关闭。
|
||||
- 浏览器因果:状态证据仍只冻结 trusted input listener 及其点击派生微任务内的变化;完整手势身份改由宿主在成功完成 Chromium 元素鼠标输入后调用隔离世界 finish。更早注册的 `window` capture listener 即使调用 `stopImmediatePropagation()` 也不能阻断探针自身的完成身份,页面脚本不能伪造 host finish,RAF / timer 继续不计入动作结果。
|
||||
- 验证边界:Linux 定向回归覆盖目录相对读写与清理、祖先 symlink、CAS 安装后错误、九文件混合快照、Tetris AST 反例和七项真实 Chrome generic 试玩。Windows cfg 代码必须继续在真实 Windows CI / 发布构建验证;本地缺少 MinGW C compiler 时,安装了 Rust target 也不能把交叉 `cargo check` 失败误报为源码失败。
|
||||
- JavaScript / ESM 循环与体积补充:投影声明按 `(origin module, original root binding)` 保存身份,canonical 重命名不能改写原始身份;删除回流声明后仍把 import 引用改接到 importer 已有 canonical,并给固定点保留“模块数 + 1”轮的产出与稳定确认预算,未收敛时失败关闭。inline module 先按浏览器可执行标签提取原文并计入与外部脚本共享的累计 `2 MiB` 源码预算,再做语法和语义校验;无效超限模块不能被 helper 静默过滤,无效小模块也明确失败关闭。
|
||||
|
||||
@@ -836,7 +836,7 @@ game-project/
|
||||
- dynamic import demand 必须以 source 与 import occurrence position 联合作为身份,同 source 的可达裸 import 不得继承不可达 occurrence 的 export。动态 namespace 在首段 export 之后继续保留完整 member path,声明 initializer 与后续赋值式 `await import` 都必须绑定 semantic symbol。named / namespace 成员作为回调参数、对象解构 alias、实例 alias 与下游 wrapper 时继续按真实调用 span 传播 demand。constructor、`new Game().method()` 和实例 alias 分别形成精确成员根,`this.method()` 只匹配相同 class / object owner;顶层声明位置只使用 Oxc span,禁止以源码文本 `find` 反查。受控 callback API 名严格大小写敏感,调用图只读取真实 callback 参数位:timer、microtask、RAF、Promise 与数组迭代 API 取第一个参数,`addEventListener` 取第二个参数,delay、initial value、event type、options 和其它额外参数都不得建立执行边。恒假分支区间由单次函数可达性 analysis 预计算、排序合并并借用二分索引,不得使用 thread-local 完整源码 key 或在每个位置查询时复制区间、重扫整份源码。
|
||||
- imported member 只有作为受控 callback API 的参数时才建立执行 demand;日志、注册元数据等普通传参不能被当作调用。普通 member alias 必须同时支持声明 initializer 与后续赋值,`new ns.Game()` 的完整 constructor path 继续传播到实例 alias。局部对象、class、实例、`this` 和 `super` 的方法调用统一按 semantic receiver owner 匹配,禁止按末级方法名跨 owner 选中同名 decoy。
|
||||
- member alias、局部 receiver 与动态 namespace 的 assignment 必须保留赋值位置和 enclosing function scope;函数体内的使用必须按真实 invocation position 选择当时已生效的事件,多次调用跨过赋值边界时合并全部可能 owner,恒假分支、未调用函数或调用之后的赋值不能倒灌覆盖更早使用点。`receiver = source` 必须冻结赋值完成时的 source owner,source 后续重赋值不能反向改写 receiver;identifier callee 与 `new C(args)` 的 constructor / instance owner 在参数求值前冻结,真实 invocation effect 仍在参数求值后生效,callable assignment 只在 RHS 完成后替换旧值,因此 `start = start()` 必须调用旧 callable。`return / throw` 的表达式先完整执行,随后才截断后续事件;函数体和截断位置统一使用 Oxc AST body / statement span,不能被默认参数或解构参数中的 `{}` 误导。未知 guard clause 后的语句与 `catch` 体赋值都保留执行和跳过状态;调用事件继续按 JavaScript 的 callee、参数 / RHS、内层调用、外层调用 / 赋值顺序生效,`outer(inner())` 与 `receiver = mutate()` 不得按 AST 先序颠倒副作用。未知条件、循环以及 `switch case/default` 内赋值保留执行与跳过状态,conditional expression 保留全部可解析 owner,无法解析的确定重赋值显式 invalidation;已调用函数对外层 alias 的最终副作用按调用位置回写,但普通函数体内无条件 `return / throw` 之后的事件必须截断。被导出的 function、class/object member 还必须把模块初始化完成视为潜在外部调用时点,使声明之后生效的顶层赋值进入 demand,同时保留此前本地调用的旧状态。动态依赖传播继续以原始 owner 模块 AST span 为权威,不得因投影重排声明或省略独立赋值语句而重算出相反 alias。class owner 必须继续区分 static / instance,class expression 与实例化 alias 进入相同 receiver 图,`super` 的 parent owner 在 class 定义位置冻结。constructor、`.call()` 与 `.apply()` 都是实际 invocation;具名 function expression 只保留外层 binding 对应的单一 range,未被受控 API 执行的 inline function / arrow 保持不可达。恒真 / 恒假关键字只按 JavaScript 大小写识别,局部或参数遮蔽的 `undefined` 不得当作恒假;恒假分支识别必须先用 parser trivia 屏蔽注释和非执行 literal。callback API 只接受 semantic 未解析的已知全局调度函数或 AST 可证明的 literal array / dynamic import 调用;用户定义、导入或遮蔽的同名 `setTimeout / map / then` 不能触发参数执行推断。
|
||||
- `new` 必须覆盖显式 `super()`、隐式 derived constructor 和直接 class expression,未 override 的 instance / static 方法沿冻结的 `extends` 链查找;普通嵌套 function 拥有独立 `this`,只有 arrow 词法继承外层 owner。声明后通过 assignment 安装的 function / arrow、本地 function alias 和 `.bind()` 结果都必须保留 callable identity;只有真正 Function.prototype 的 `.call()` / `.apply()` / `.bind()` 采用 callable 语义,业务对象同名方法仍按 receiver method 解析。callable conditional expression 必须合并 consequent / alternate 的全部可调用身份;声明式和后续赋值式 `api.run.bind(api)` 都在绑定时冻结成员 owner 与方法名。受控 callback 识别穿透 callee 与 receiver 外层括号;Promise `.then` 的 fulfilled / rejected 两个参数都是 callback,`.catch` / `.finally` 仍仅取第一个。视觉资产启发式允许使用 ASCII 小写副本查找路径、`drawImage` 与图片变量,但函数调用图、semantic binding 和可达性必须始终解析原始大小写 JavaScript,禁止把 `MainLoop` 与 `mainloop` 合并。
|
||||
- `new` 必须覆盖显式 `super()`、隐式 derived constructor 和直接 class expression,未 override 的 instance / static 方法沿冻结的 `extends` 链查找;普通嵌套 function 拥有独立 `this`,只有 arrow 词法继承外层 owner。声明后通过 assignment 安装的 function / arrow、本地 function alias 和 `.bind()` 结果都必须保留 callable identity;只有真正 Function.prototype 的 `.call()` / `.apply()` / `.bind()` 采用 callable 语义,业务对象同名方法仍按 receiver method 解析。callable conditional expression 必须合并 consequent / alternate 的全部可调用身份;声明式和后续赋值式 `api.run.bind(api)` 都在绑定时冻结成员 owner 与方法名。受控 callback 识别穿透 callee 与 receiver 外层括号;Promise `.then` 的 fulfilled / rejected 两个参数都是 callback,`.catch` / `.finally` 仍仅取第一个。视觉资产启发式允许使用 ASCII 小写副本查找路径、`drawImage` 与图片变量,但函数调用图、semantic binding 和可达性必须始终解析原始大小写 JavaScript,禁止把 `MainLoop` 与 `mainloop` 合并。Canvas 视觉门必须复用同一组受 256 文件、累计源码 `2 MiB` 与投影处理 `32 MiB` 限制的 inline / external module 加载、链接和投影结果,并逐个原始大小写 unit 独立关联资产路径、图片变量与可达 `drawImage`;`<script type="module">`、本地 `<script type="module" src>` 及其真实可达依赖可以形成证据,未链接文件、恒假动态依赖、未调用函数、跨 unit 拼接和仅出现在 HTML 文本中的路径不得借用其它 unit 的绘制调用过门。
|
||||
- conditional callable 的 consequent / alternate 必须在 test 求值之后分别按自身表达式起点冻结身份;受控 callback 参数也按该参数自身起点解析,使更早参数的 alias 副作用先行生效,同时保持真实 callback 执行边位于注册调用之后。
|
||||
- 泥点不足是确定性业务中断,不是瞬态 Provider 故障或未知副作用。钱包的 `泥点余额不足` 与 `可消费泥点不足:...` 两种领域文案统一映射为稳定原因 `mud-points-insufficient`,不得自动重试;即使 External Generation durable ledger 已存在,也必须落为 `failed`,不能误入 `needs-reconciliation`。game-chat 顶部状态、持久失败对话与 `【Supervisor 阶段记录】` 统一显示“泥点余额不足,本轮游戏生成已中断。请充值后发送“继续”,系统会从当前项目进度接着完成。”,并禁止透传 operationId、URL、路径、密钥或任意上游正文。
|
||||
- tool-plan 成功响应落账前,对内置 Runtime 原生函数与 legacy wrapper 的合法、无重复 key JSON arguments 按工具 schema 的精确位置做项目路径 canonicalization:`file.*.path`、`project.patchset.changes[*].path`、`project.git_commit.paths[*]`、`command.*.cwd`、`image.inspect.paths[*]` 与 `canvas.asset_generate.outputPath` 若是当前项目根目录内的完整绝对路径,转换为 `/` 分隔的项目相对路径后再校验、持久化并执行;源码/叙述字段、任务产物描述、动态 MCP arguments 和项目外绝对路径不得改写,后两者继续由绝对路径门禁失败关闭。项目根只允许搜索/列举范围与命令 cwd 规范化为 `.`,不能成为文件目标。当前进程与重启恢复都必须从同一份规范化 handoff 重放,禁止分别执行原响应和持久响应。
|
||||
|
||||
Reference in New Issue
Block a user