8a8d94c14a
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 16s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 16s
Project CI / AI game creator shell Rust smoke (pull_request) Failing after 12s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 16s
Project CI / Backend tests (pull_request) Failing after 21s
Project CI / Native shell tests (pull_request) Failing after 20s
Project CI / Frontend tests (pull_request) Failing after 8s
Project CI / AI game creator shell web tests (pull_request) Failing after 17s
Project CI / Repository checks (pull_request) Failing after 17s
- 自动环境预检接入首页与 Direct 后端:新建 Web 游戏在生成前完成 Node/npm、真实构建与双端浏览器检查 - 新增宿主权威执行账本与交付合同:冻结核验项、绑定当前输入证据、封口后拒绝新副作用并产出宿主报告 - 原生 shell、内置浏览器/托管命令与第三方 MCP 共用执行许可和累计执行时间预算,按执行批次与墙钟分别约束 - 新增固定种子 runner 物理基线、真实双端输入与公平窗口负例,可复用且不冒充完整关卡 - 新增请求与工具分段计时、有界并行批读和首轮上下文预取,未知耗时不补零 - 移除 SDK 全局串行的 apply_patch/update_plan 注册,改由宿主 agc_apply_patch/agc_update_plan 提供等价能力,独立工具可并发 - 付费提交与本地写入绑定原回合租约:封口、取消或耗尽后零新增提交,已受理操作保留 GET 对账 - 捆绑 Codex 由 0.147.0 升级到 0.155.1,固定版本收敛到 build_support/codex_bundle.rs,vendor 解析源码按 rust-v0.155.1 重取并更新 UPSTREAM 证据 - 适配 0.155 统一 exec(exec_command/write_stdin),生产夹具新增会话用例,宿主 Job 与期限约束保持有效 - 同步更新 AGC 主规范、Skill 与提示词、共享记忆和发行载荷校验
149 lines
4.5 KiB
JavaScript
149 lines
4.5 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import { test } from 'node:test';
|
|
|
|
import {
|
|
advanceRunner,
|
|
createRunner,
|
|
createRunnerRandom,
|
|
measuredRunnerFairWindowMs,
|
|
restartRunner,
|
|
RUNNER_SEED,
|
|
runnerProjection,
|
|
runnerSeedFromSearch,
|
|
setRunnerInput,
|
|
startRunner,
|
|
stepRunner,
|
|
} from '../src-tauri/resources/agc-skills/agc-browser-playtest/references/runner-physics.mjs';
|
|
|
|
function jump(
|
|
settings,
|
|
holdTicks,
|
|
functions = {
|
|
createRunner,
|
|
startRunner,
|
|
setRunnerInput,
|
|
stepRunner,
|
|
runnerProjection,
|
|
},
|
|
) {
|
|
const state = functions.createRunner(RUNNER_SEED, settings);
|
|
functions.startRunner(state);
|
|
functions.setRunnerInput(state, 'jump', true);
|
|
const samples = [];
|
|
for (let tick = 0; tick < 150; tick += 1) {
|
|
if (tick === holdTicks) functions.setRunnerInput(state, 'jump', false);
|
|
functions.stepRunner(state);
|
|
samples.push(functions.runnerProjection(state));
|
|
if (state.onGround && state.jumpCount > 0) break;
|
|
}
|
|
return {
|
|
state,
|
|
samples,
|
|
height: Math.max(
|
|
...samples.map((sample) => sample.groundY - sample.playerY),
|
|
),
|
|
};
|
|
}
|
|
|
|
test('fixed seed, fixed steps and independent decoration produce the same course', () => {
|
|
assert.equal(runnerSeedFromSearch('?agcPlaytestSeed=20260920'), RUNNER_SEED);
|
|
assert.throws(() =>
|
|
runnerSeedFromSearch('?agcPlaytestSeed=1&agcPlaytestSeed=2'),
|
|
);
|
|
assert.throws(() => runnerSeedFromSearch('?agcPlaytestSeed=4294967296'));
|
|
const first = createRunner();
|
|
const decoration = createRunnerRandom(71);
|
|
for (let index = 0; index < 100; index += 1) decoration();
|
|
const second = createRunner();
|
|
assert.deepEqual(first.course, second.course);
|
|
assert.notEqual(
|
|
first.courseFingerprint,
|
|
createRunner(20260921).courseFingerprint,
|
|
);
|
|
startRunner(first);
|
|
startRunner(second);
|
|
for (let index = 0; index < 60; index += 1) advanceRunner(first, 1 / 60);
|
|
for (let index = 0; index < 120; index += 1) advanceRunner(second, 1 / 120);
|
|
assert.deepEqual(runnerProjection(first), runnerProjection(second));
|
|
const course = structuredClone(first.course);
|
|
restartRunner(first);
|
|
assert.equal(first.phase, 'ready');
|
|
assert.equal(first.simulationTick, 0);
|
|
assert.equal(first.jumpCount, 0);
|
|
assert.deepEqual(first.course, course);
|
|
});
|
|
|
|
test('short jump cuts once, long jump rises higher, release restores slide collision size', () => {
|
|
const short = jump({}, 4);
|
|
const long = jump({}, 18);
|
|
assert.equal(short.state.jumpCount, 1);
|
|
assert.equal(short.state.jumpCutCount, 1);
|
|
assert.ok(long.height > short.height + 10);
|
|
assert.ok(short.state.onGround && long.state.onGround);
|
|
const state = short.state;
|
|
setRunnerInput(state, 'slide', true);
|
|
stepRunner(state);
|
|
assert.ok(state.sliding && state.playerHeight < state.config.playerHeight);
|
|
setRunnerInput(state, 'slide', false);
|
|
stepRunner(state);
|
|
assert.ok(!state.sliding && !state.slideHeld);
|
|
assert.equal(state.playerHeight, state.config.playerHeight);
|
|
});
|
|
|
|
test('baseline has at least 180ms clearance while the original narrow-window parameters fail', () => {
|
|
const baseline = jump({}, 4);
|
|
assert.ok(
|
|
measuredRunnerFairWindowMs(
|
|
baseline.samples,
|
|
49,
|
|
baseline.state.course[0],
|
|
) >= 180,
|
|
);
|
|
const original = jump(
|
|
{
|
|
gravity: 2300,
|
|
jumpVelocity: 800,
|
|
releaseVelocity: 720,
|
|
playerWidth: 48.9,
|
|
},
|
|
1,
|
|
);
|
|
const window = measuredRunnerFairWindowMs(
|
|
original.samples,
|
|
48.9,
|
|
original.state.course[0],
|
|
);
|
|
assert.ok(window < 180, `original jump window ${window}ms must fail`);
|
|
});
|
|
|
|
test('regression mutations expose repeated jump-cut and ignored slide release', async () => {
|
|
const source = fs.readFileSync(
|
|
new URL(
|
|
'../src-tauri/resources/agc-skills/agc-browser-playtest/references/runner-physics.mjs',
|
|
import.meta.url,
|
|
),
|
|
'utf8',
|
|
);
|
|
const repeatedCut = source.replace('&& !state.jumpCut)', ')');
|
|
assert.notEqual(repeatedCut, source);
|
|
const broken = await import(
|
|
`data:text/javascript;base64,${Buffer.from(repeatedCut).toString('base64')}`
|
|
);
|
|
assert.ok(jump({}, 4, broken).state.jumpCutCount > 1);
|
|
const noRelease = source.replace(
|
|
'state.slideHeld = Boolean(held);',
|
|
'if (held) state.slideHeld = true;',
|
|
);
|
|
const stuck = await import(
|
|
`data:text/javascript;base64,${Buffer.from(noRelease).toString('base64')}`
|
|
);
|
|
const state = stuck.createRunner();
|
|
stuck.startRunner(state);
|
|
stuck.setRunnerInput(state, 'slide', true);
|
|
stuck.stepRunner(state);
|
|
stuck.setRunnerInput(state, 'slide', false);
|
|
stuck.stepRunner(state);
|
|
assert.equal(state.sliding, true);
|
|
});
|