diff --git a/tier2/engine/boot-phaser-host.js b/tier2/engine/boot-phaser-host.js index dc07172b..2514612e 100644 --- a/tier2/engine/boot-phaser-host.js +++ b/tier2/engine/boot-phaser-host.js @@ -203,6 +203,9 @@ export async function bootPhaserHost(opts) { // 受控能力面经 boot 上下文额外透传(游戏经 ctx.getEngine() 拿;headless 下 no-op 能力)。 // 注:契约 PhaserHostBootContext 不含 caps 字段,caps 经 game 内部约定取;此处 additive 透传不破契约必填项。 caps, + // recHook additive 透传:游戏逻辑(如资源系统完单/合成)经它把 call-ID 记进 window.__engineCalls, + // 供 F 门 / 富游戏三联动门证「真调资源(addCoins/consumeIngredient)、非自绘伪装」(同 caps 不破契约必填项)。 + recHook, })); // 就绪钩子(harness 可挂取证全局;headless 下用于逻辑校验)。 onReady(host, { game, phaserGame: null, canvas: null, caps, uncaught, latchInfo, pollLatch }); @@ -248,6 +251,7 @@ export async function bootPhaserHost(opts) { seed, assets, caps, // additive 透传受控面(游戏经 ctx.getEngine() 拿;不破契约必填项) + recHook, // additive 透传:游戏逻辑经它记 call-ID 进 __engineCalls(F 门 / 三联动门真接线证据) })); // 挂逐帧 latch 轮询:Phaser scene events 'postupdate' 每帧后调(A4 铁律核心)。 self.events.on('postupdate', pollLatch); diff --git a/tier2/fixtures/mini-fei-e/src/game-core.js b/tier2/fixtures/mini-fei-e/src/game-core.js index c7b8d4cb..61ef42c5 100644 --- a/tier2/fixtures/mini-fei-e/src/game-core.js +++ b/tier2/fixtures/mini-fei-e/src/game-core.js @@ -44,9 +44,11 @@ export function createGameCore(deps) { if (!rng || typeof rng.next !== 'function') { throw new Error('[game-core] 必须注入受控随机源 random(next/range)'); // 错误路径留痕 } + // call-ID 取证钩子(host 经 main.js 透传;缺省 no-op)。资源系统据它把 'resource.*' 记进 __engineCalls(F 门/三联动门)。 + const recHook = deps.recHook || (() => {}); // 三系统装配(资源是中心,合成 / 订单都注入它 → 耦合点 3/4/5 真接线)。 - const resource = createResourceSystem({ random: rng, startIngredients: START_INGREDIENTS, startCoins: START_COINS }); + const resource = createResourceSystem({ random: rng, startIngredients: START_INGREDIENTS, startCoins: START_COINS, recHook }); const merge = createMergeSystem({ resource, chains: MERGE_CHAINS, random: rng }); const order = createOrderSystem({ resource, templates: ORDER_TEMPLATES, activeCount: ACTIVE_ORDER_COUNT, loseStreak: LOSE_STREAK, random: rng, diff --git a/tier2/fixtures/mini-fei-e/src/main.js b/tier2/fixtures/mini-fei-e/src/main.js index 6490eccd..00f352d8 100644 --- a/tier2/fixtures/mini-fei-e/src/main.js +++ b/tier2/fixtures/mini-fei-e/src/main.js @@ -54,8 +54,10 @@ export default function createGame(opts) { async boot(ctx) { // 据 boot.seed 派生受控随机源(确定性;禁裸 Math.random)。 const random = createSeededRandom(ctx.seed >>> 0); - // 建游戏核心(三系统逻辑,平台预建写死)。 - core = createGameCore({ random }); + // 建游戏核心(三系统逻辑,平台预建写死)。recHook 由 host 经 boot 上下文 additive 透传: + // 资源系统完单/合成时经它把 'resource.addCoins'/'resource.consumeIngredient' 记进 window.__engineCalls, + // 供 F 门 / 富游戏三联动门证真接线(headless 下 ctx.recHook 缺省 → no-op)。 + core = createGameCore({ random, recHook: ctx.recHook }); core.start(); // title → play(spike 直接进局;产线可加标题页) // 受控能力面(host 经 boot 上下文 additive 透传 caps;headless 下为 no-op 能力面)。 diff --git a/tier2/fixtures/mini-fei-e/src/systems/resource-system.js b/tier2/fixtures/mini-fei-e/src/systems/resource-system.js index ecb4c264..540b064b 100644 --- a/tier2/fixtures/mini-fei-e/src/systems/resource-system.js +++ b/tier2/fixtures/mini-fei-e/src/systems/resource-system.js @@ -28,6 +28,8 @@ * @param {{ next:()=>number, range:(a:number,b:number)=>number }} [deps.random] 受控随机源(资源系统本身少用随机,预留) * @param {Record} deps.startIngredients 开局库存 * @param {number} deps.startCoins 开局金币 + * @param {(id:string)=>void} [deps.recHook] call-ID 取证钩子(host 经 boot 透传 → window.__engineCalls); + * 每次真调命令记 'resource.' call-ID,供 F 门 / 富游戏三联动门(callsAddCoins/callsConsumeIngredient)证真接线。 * @returns {ResourceSystem} */ export function createResourceSystem(deps) { @@ -38,6 +40,9 @@ export function createResourceSystem(deps) { ingredients: { ...(deps.startIngredients || {}) }, }; + // call-ID 取证钩子(缺省 no-op;headless / 静态校验下无 host → no-op,不抛错)。 + const recHook = (deps && deps.recHook) || (() => {}); + // F 门真接线取证:四个命令的真实调用计数(机器证据,agent 自述不作数)。 const _callCounts = { addCoins: 0, spendCoins: 0, consumeIngredient: 0, addIngredient: 0 }; @@ -52,6 +57,7 @@ export function createResourceSystem(deps) { addCoins(n) { // 受控日志锚:金币变动是经济门核心,留可追溯计数。 _callCounts.addCoins++; + recHook('resource.addCoins'); // 真接线 call-ID(F 门 / 三联动门 callsAddCoins 读 __engineCalls) state.coins += Math.max(0, n | 0); }, @@ -80,6 +86,7 @@ export function createResourceSystem(deps) { const have = state.ingredients[itemId] || 0; if (have < need) return { ok: false, reason: 'no_stock' }; // 库存不足 → 失败 state.ingredients[itemId] = have - need; + recHook('resource.consumeIngredient'); // 真接线 call-ID(三联动门 callsConsumeIngredient 读 __engineCalls;仅真扣成功记) return { ok: true }; },