From 2fa952358736e33214b08d9b6c8bd7894e57f77a Mon Sep 17 00:00:00 2001 From: john Date: Wed, 9 Sep 2026 22:10:19 +0800 Subject: [PATCH] feat(goose-v149): add offline phase3 gate without real LLM calls Block unattended DashScope smokes by default and provide a zero-token merge verification entry via run-goosed-v149-phase3-offline.mjs. Co-authored-by: Cursor --- docs/goose-v149-phase3-closeout.md | 14 ++- docs/goose-v149-real-llm-gate.md | 10 +- scripts/run-goosed-v149-phase3-offline.mjs | 121 +++++++++++++++++++++ 3 files changed, 141 insertions(+), 4 deletions(-) create mode 100644 scripts/run-goosed-v149-phase3-offline.mjs diff --git a/docs/goose-v149-phase3-closeout.md b/docs/goose-v149-phase3-closeout.md index 57c5eed..db328d7 100644 --- a/docs/goose-v149-phase3-closeout.md +++ b/docs/goose-v149-phase3-closeout.md @@ -7,9 +7,19 @@ ## 聚合验证入口 +**默认(不消耗 LLM token):** + ```bash -bash scripts/run-goosed-v149-local.sh # :18049 另开终端 -node scripts/run-goosed-v149-phase3.mjs # Phase 2 + parity + drift +node scripts/run-goosed-v149-phase3-offline.mjs +``` + +通过时应输出 **`GOOSE_V149_PHASE3_OFFLINE_OK`**。 + +**完整 smoke(须用户明确批准 + DashScope 配额):** + +```bash +GOOSE_V149_ALLOW_REAL_LLM=1 bash scripts/run-goosed-v149-local.sh # :18049 另开终端 +GOOSE_V149_ALLOW_REAL_LLM=1 node scripts/run-goosed-v149-phase3.mjs ``` 通过时应输出 **`GOOSE_V149_PHASE3_OK`**。 diff --git a/docs/goose-v149-real-llm-gate.md b/docs/goose-v149-real-llm-gate.md index 184d0ef..bb0bf11 100644 --- a/docs/goose-v149-real-llm-gate.md +++ b/docs/goose-v149-real-llm-gate.md @@ -37,8 +37,14 @@ GOOSE_V149_ALLOW_REAL_LLM=1 node scripts/check-goosed-v149-reply-smoke.mjs ## 推荐替代 -- 合并前优先:`node --test scripts/goose-v149-canary.test.mjs`、`compare-goose-v149-message-sanitize.mjs`、`compare-goose-v149-memory-manifest.mjs` -- 必须验证 Portal 集成时:单次、有人值守、先确认 DashScope 配额 +- **零 LLM 合并前验证**(默认推荐): + +```bash +node scripts/run-goosed-v149-phase3-offline.mjs +``` + +- 单项:`node --test scripts/goose-v149-canary.test.mjs`、`compare-goose-v149-message-sanitize.mjs`、`compare-goose-v149-memory-manifest.mjs` +- 必须验证 Portal 集成时:单次、有人值守、先确认 DashScope 配额,且 `GOOSE_V149_ALLOW_REAL_LLM=1` ## 回滚 Canary diff --git a/scripts/run-goosed-v149-phase3-offline.mjs b/scripts/run-goosed-v149-phase3-offline.mjs new file mode 100644 index 0000000..6ef8602 --- /dev/null +++ b/scripts/run-goosed-v149-phase3-offline.mjs @@ -0,0 +1,121 @@ +#!/usr/bin/env node +/** + * Phase 3 offline closeout: unit tests + manifest drift only (no real LLM). + * Use when DashScope quota is exhausted or unattended smokes must stay blocked. + * + * Full portal/page/memory smokes still require: + * GOOSE_V149_ALLOW_REAL_LLM=1 node scripts/run-goosed-v149-phase3.mjs + */ +import { spawnSync } from 'node:child_process'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { prepareGooseV149CheckEnv } from './goose-v149-canary.mjs'; +import { checkPassed, classifyCheckResult } from './goose-v149-check-result.mjs'; + +const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..'); +const checkEnv = prepareGooseV149CheckEnv({ ...process.env }, root); + +const checks = [ + { + name: 'real-llm-gate-unit', + command: [process.execPath, '--test', 'scripts/goose-v149-real-llm-gate.test.mjs'], + required: true, + }, + { + name: 'deepseek-parity', + command: [process.execPath, '--test', 'release-gate/deepseek-production-parity.test.mjs'], + required: true, + }, + { + name: 'goose-canary-unit', + command: [process.execPath, '--test', 'scripts/goose-v149-canary.test.mjs'], + required: true, + }, + { + name: 'memory-manifest-unit', + command: [process.execPath, '--test', 'scripts/compare-goose-v149-memory-manifest.test.mjs'], + required: true, + }, + { + name: 'memory-drift', + script: 'compare-goose-v149-memory-manifest.mjs', + required: true, + }, + { + name: 'memory-failopen', + script: 'check-goosed-v149-memory-failopen.mjs', + required: true, + }, + { + name: 'context-runtime-unit', + command: [ + process.execPath, + '--test', + 'memind-headroom-policy.test.mjs', + 'context-budget.test.mjs', + 'recall-fusion.test.mjs', + 'mcp-result-compactor.test.mjs', + ], + required: true, + }, +]; + +function runScript(script, extraEnv = {}) { + const result = spawnSync(process.execPath, [path.join(root, 'scripts', script)], { + cwd: root, + env: { ...checkEnv, ...extraEnv }, + encoding: 'utf8', + }); + return { + ok: checkPassed(result), + outcome: classifyCheckResult(result), + status: result.status, + stdout: result.stdout?.trim() ?? '', + stderr: result.stderr?.trim() ?? '', + }; +} + +function runCommand(command, extraEnv = {}) { + const [bin, ...args] = command; + const result = spawnSync(bin, args, { + cwd: root, + env: { ...checkEnv, ...extraEnv }, + encoding: 'utf8', + }); + return { + ok: checkPassed(result), + outcome: classifyCheckResult(result), + status: result.status, + stdout: result.stdout?.trim() ?? '', + stderr: result.stderr?.trim() ?? '', + }; +} + +function main() { + const results = []; + + for (const check of checks) { + const item = { + ...check, + ...(check.script + ? runScript(check.script, check.extraEnv ?? {}) + : runCommand(check.command, check.extraEnv ?? {})), + }; + results.push(item); + console.log(`[goose-v149-offline] ${item.outcome} ${check.name}`); + if (item.stdout) console.log(item.stdout); + if (!item.ok && item.stderr) console.error(item.stderr); + } + + const failedRequired = results.filter((item) => item.required && !item.ok); + if (failedRequired.length) { + console.error(`GOOSE_V149_PHASE3_OFFLINE_FAIL: ${failedRequired.map((item) => item.name).join(', ')}`); + process.exit(1); + } + + console.log('GOOSE_V149_PHASE3_OFFLINE_OK'); + console.log(' note: portal/page/memory smokes skipped; see docs/goose-v149-real-llm-gate.md'); +} + +main();