修复 master CI 红:收银台深链前缀路由、外部 MCP 工具数断言、并发 span 采集竞态、AGC 壳 shard-4 断言 #609
Reference in New Issue
Block a user
Delete Branch "fix/ci-master-red"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
这个 PR 修什么
修 master(
ccd271572)上已红的检查项,外加两处会真实影响线上/网关行为的漂移与 AGC 壳 shard-4 的偶发断言。不放宽断言、不 sleep 赌时序、不起 dev 栈。1. Nginx SPA allowlist 缺
/pay、/profile/payment,收银台深链/pay/<checkoutToken>仍然 404为什么红:
src/routing/activeAppPageRoutes.ts的STAGE_ROUTE_ENTRIES早已把payment-checkout → /pay、profile-payment → /profile/payment定义为主站 SPA 路由,三份模板的 allowlist 没跟上 →check:nginx-spa-routes失败。更严重的同一处漂移:
payment.rs生成的checkoutUrl是format!("/pay/{}", checkout_token),前端用startsWith('/pay/')判定并取最后一个路径段当 token;只把裸前缀加进 allowlist 时深链会落默认location /的try_files $uri $uri/ =404→ 真实收银台链接 404。同批漂移里check:pingora-route-parity也红(PingoraMAIN_SPA_PATHS缺/pay、/profile/payment),只是被 lint 链里先失败的门禁掩盖。改法:前缀路由进真相源 ——
APP_PREFIX_ROUTE_ENTRIES('/pay' → payment-checkout)+resolveSelectionStageFromPath改用它;location ~* "^/pay/[^/]+/?$"(裸前缀仍由精确 location 负责;前缀 location 镜像精确 location 的维护闸与try_files $uri /index.html =404;);check-nginx-spa-routes按真相源强制前缀形状,新增scripts/check-nginx-spa-routes.test.mjs正/反用例(写成精确匹配^/pay/?$或过宽^/pay都会红),由npm run check:nginx-spa-routes一起跑;MAIN_SPA_PATHS补/pay、/profile/payment,新增MAIN_SPA_PREFIX_PATHS+is_main_spa_prefix_path(大小写不敏感、只认「前缀 + 恰好一段」),矩阵新增pay_checkout_spa_fallback用例(/pay/checkout-token→ static/web/spa_fallback)+ 网关前缀正/反单测;check-pingora-route-parity增加MAIN_SPA_PREFIX_PATHS与前端逐条比对;deploy/nginx/README、本地开发与生产运维文档(含线上curl /pay/<token>复验方式)。本地实跑:
2.
external_mcp::semantic写死的 legacy 工具数 30 → 324b529a895(支付服务接入)在内置 OpenAPI 新增createExternalPaymentOrder、getExternalPaymentOrder两条未被x-mcp-excluded排除的操作,legacy 可调用工具由 30 合法增至 32(共 37 条操作 − 5 条元数据/入口操作)。断言改为 32 并注明来源,保留「语义工具不得覆盖 legacy 工具」的原意。总数断言两次修正:先是改成派生式,评审指出BTreeSet构造下恒真后又改回字面量47(32 + 15)。3. 并发测试下 HTTP/Provider span 偶发采集为空(tracing callsite interest 竞态)
根因(代码级链条,tracing 0.1.44 / tracing-core 0.1.36):
span!/info_span!在 callsite 缓存 interest 为never时静默返回空 span,连new_span都不调(tracing-0.1.44/src/macros.rs的span!分支);DefaultCallsite的 interest 只在调用点首次命中时算一次,计算时用DISPATCHERS.rebuilder(),进程里只注册过一个 dispatcher 时退化成dispatcher::get_default(),即命中线程自己的 dispatcher(tracing-core-0.1.36/callsite.rs的Rebuilder::JustOne);build_router用例、platform-llm 大量直连客户端用例)抢到http.request/llm.request首次注册就会把它永久缓存成never→ 测试看到 0 个 span。这正是「同批代码时而绿时而红」。修法:
set_default与with_subscriber在 interest 缓存上等价(都只是新建Dispatch触发一次rebuild_interest,只能纠正「已经注册过」的调用点),所以关键是在自家 subscriber 下命中同一调用点热身:app::tests::http_tracing:请求前用同一http.request调用点热身到连续两轮采集成功,并在整个请求期间持有 scoped default;observability_tests:run_under_capture固定整段流程的 scoped default +warm_up_provider_span_callsite(指向刚释放 loopback 端口的最小失败请求命中同一llm.request调用点,连续两轮采集成功才继续);register_callsite只用于避免本 subscriber 触发的重建把其它调用点永久标成never;llm.requestspan),无 sleep。本地实跑:
未复现说明:该竞态窗口极窄,本机在 origin/master 上跑
app::tests::×12 与全量 bin×6 都没复现(CI 只偶发一次);结论来自上面的代码级链条 + CI 现场日志(master-backend-19291.log的left: 0 / right: 1)。4. AGC 壳 Rust shard-4 的两条偶发断言(#607 lane 2/2)
DIRECT_ACTIVE_TURNS_EVENT_TEST_COUNT曾在 2026-10-01 按线程作用域隔离,2026-10-02 退役runtime_driver搬进agent/direct_events.rs时降级回进程级static AtomicU64,断言会取到宿主tauri::async_runtime后台回合在别的线程上的广播(--test-threads=1只串行测试线程)。改回thread_local! Cell。证据:修复前把计数器临时改回 AtomicU64 → 同一并行口径 17/20 红(
thread_manager/mod.rs:1796/1802/1817/1827多条「通知」断言);修复后并行 20 次 0 红、agent::thread_manager::5 连跑 72 passed。exit 0,同组后代仍存活,trampoline 从 leader 被回收起开始PROCESS_SESSION_TARGET_TERMINATE_GRACE_MS=800ms宽限;客户端只要晚于宽限才发出 terminate 就只能读到exited。改为 leader 用wait等后台子进程(trap /sleep 0.4/ marker / 断言均未改)。验证:该用例是
#[cfg(target_os = "linux")],Windows 本机跑不到;已在真实 Linux 内核(WSL Alpine)验证命令形状:leader 活到 TERM、同组后代完成 400ms 延迟清理(marker=done,real 0.41s)、清理后组内零残留。CI 侧仍需跑node apps/ai-game-creator-shell/scripts/run-rust-shell-test-shards.mjs --shards=4 --shard-index=4复核。pitfalls.md:更新 graceful terminate 已过时的验证口径,补三条 2026-10-04 条目(tracing interest 竞态、AGC 偶发串台、SPA 深链前缀路由)。收尾自检(本地实跑)
未验证 / 存疑项
cargo test --workspace:bgfilter_worker::tests::cold_start_flat_retries_past_regular_quota_until_worker_listens挂起 >60s、wallet_refund_outbox::tests::worker_recovers_temp_file_immediately_on_startup失败(均依赖外部环境,master 的 Linux CI 上是绿的),与本 PR 改动无关;workspace 级结论以 CI 为准。process_session用例本机无法执行(见上)。project::export::npm_export_tests::npm_package_contains_only_dist_and_publish_readme(2026-10-03 的多次 lane run 里左右值差一个assets/hero.png)不在本 PR 范围,疑似用例间共享临时目录/环境变量,仅记录待另行排查。合并顺序
本 PR 必须先合,再合
fix/agc-canvas-reference-insert、fix/home-project-naming-async、fix/agc-composer-layout(它们会与本次pitfalls.md尾部追加与 Nginx/Pingora 门禁同文件冲突)。/pay/<checkoutToken>的前缀路由(Nginx 三模板 + Pingora + 门禁) ad0430fcd2