增加不可变源码检出、两阶段 finalize/activate、Runtime 同源对象读取与 iframe 安全边界,并用 fail-closed CI 门固定契约。Git 仅提交平台实现、契约、迁移和 SoT,不包含具体游戏源码、素材、bundle 或验收证据。
31 KiB
name, description
| name | description |
|---|---|
| staging-ops | 在 mini-desktop/mini-infra 上做 staging 或内测 dev 的部署运维时使用:机器分工铁律、代码同步(git push→Gitea clone)、后端重部署标准序、构建门/冒烟门、观测栈起容器、生成线环境地图与 executor 接线的实战配方与踩坑。 |
staging 运维配方(机器分工 / 代码同步 / 重部署 / 构建门 / 冒烟)
staging 全栈在 mini-desktop(100.64.0.7):huijing 单体 :48080 + game-studio :4173 + game-admin :4174 + 隔离 MySQL/Redis 容器;共享基建(Gitea/MySQL/Redis/MinIO/new-api/Nacos/RocketMQ/观测栈五件 Collector/Tempo/Prometheus/Grafana/Loki)在 mini-infra(100.64.0.8)。 本配方由 2026-06-09~11 多波实战蒸馏(M1 真跑 / 黄金闭环 / UI 走查 / Wave4 集成门);红线类条目同步收录于
../rules/engineering-conventions.md。真 UI 走查另见ui-walkthrough-cdp.md。
1. 机器分工铁律(四机)
- 创始人本地工作站 → lili-mac(M1 Pro / 32G / ARM;不入 Tailscale 调度,会休眠合盖):跑 Claude Code 主会话、本地全栈 dev、本地浏览器/CDP 快走查、
mvn/npm快构建迭代。两条边界:① ARM≠x86,Mac 产出的 JAR/前端 bundle 架构中立可用,但 Docker 镜像 + smoke 门必须在 mini-desktop x86 出(否则镜像架构错、与 prod 不同构=半假门);② Mac 上 CDP 只算"快走查"加速开发,e2e 验收门仍只认 mini-desktop 结果(服务+浏览器同机 localhost,见game-e2e-cdp-harness.md)。 - 权威验收 / 重型构建 / 测试 / staging app / 浏览器 e2e 门 → mini-desktop(15G;java17 + mvn3.8.7 + docker + node22 + chrome146;与 prod 同构)。
- 常驻无人值守 → 6c6g:常开的 agent 编排/Workflow、cron 定时、后台批跑、git 操作(Mac 会休眠故常驻活只能留此)。禁跑重活:重型前端构建 OOM、chrome 连续 exit 144。元规则:同一失败签名第二次出现 → 换机器,不再换启动姿势。
- 共享基建 → mini-infra(绝不在其上跑项目 app / 重型构建;Nacos/RocketMQ 已自托管部署,2026-07-01)。
2. 代码同步(分类器约束)
-
从 Claude Code 环境 ssh 内网(2026-06-21 实证):
ssh mini-desktop别名会被解析到陈旧代理段198.18.0.7、port 22 被关(Connection closed by 198.18.0.7 port 22,exit 255),Bash 沙箱开/关都一样。正解 = 直连 Tailscale IP + 禁 ProxyCommand/Jump + 关沙箱:ssh -o ProxyCommand=none -o ProxyJump=none -o StrictHostKeyChecking=accept-new root@100.64.0.7 ...,且 Bash 工具设dangerouslyDisableSandbox。其它内网机(new-api100.64.0.8等)同理用直连 IP,别用可能陈旧的主机别名。 -
scp/rsync源码树会被分类器语义拦截(settings 放行也没用)。正解 = git push 到 Gitea → mini-desktop 匿名 HTTP clone/pull:http://101.200.34.71:3000/zizi-al/games-development-ai.git(:3000 HTTP 匿名读,非 :2222 SSH)。 -
单文件传输:
cat local | ssh mini-desktop 'cat >/tmp/x'(分类器不拦单文件)。 -
远程脚本省心写法:
ssh mini-desktop 'bash -ls' <<'REMOTE' ... REMOTE(-l拿 PATH,-s读 stdin,免引号地狱)。 -
push 竞态与管道掩码(2026-06-12 实翻):①大资产 push 在途时再发 push 会撞 Gitea ref 锁(
remote rejected (failed to update ref)/failed to push some refs)——同仓 push 串行化,等上一笔落地(git ls-remote核)再发;②git push 2>&1 | tail -1的退出码=tail 恒 0,会吞掉推送失败——push 不接管道,要么裸跑判$?,要么tee+PIPESTATUS[0]。每次 push 后以git ls-remote origin <branch>实证远端头,不信本地输出。 -
commit 在途 M 触发 pre-commit 版本闸,git log 不前进 ≠ commit 成功(2026-07 实证):commit 在途 M 文件(如
contracts/prompts/registry.yaml)时,pre-commit 的 Prompt Registry 一致性检查会比暂存 registry 的 version 登记 vs 工作树 prompt 正文 frontmatter version(门机制见prompt-governance.md§2 的check_registry.py)。若上个会话升了 prompt frontmatter 版本(如04-config/cheap-system.md1.8.0→1.8.1)却没同步 registry 登记,这笔 commit 就被拦[FAIL]版本漂移——git log 不前进、暂存区保留,极易误以为 commit 成功了。做法:commit 前先机械对齐 registry 登记的 version 值匹配 frontmatter(注释标明「同步 frontmatter 在途升版,变更见该文件」,不动 prompt 正文内容)。两条连带纪律:① commit 在途 M 会带上之前会话在同一工作线上的在途修改(同一工作线无法 hunk 分离,合理);② 但导致红测试的根因文件(如文案漂移的cheap_roles.py/cheap-system.md、brief 漂移的test_match3_gold_batch.py)不要 commit,免把红测试入库;量大的 raw 审计产物留 untracked。
3. 后端重部署标准序(授权窗口内执行)
教训:
~/game-staging/repo曾是 stale 克隆,「构建源 ≠ 运行 jar」翻过车——每次部署按此序,以字节码实证收口。
- 备份运行 jar 到
/tmp; - 清本地独有 untracked(
application-staging.yaml以 origin 版为准——origin 是含 executor/passport 段的超集); git reset --hard origin/dev/2.0.0;mvn clean install -pl huijing-server -am -DskipTests(warm .m2 约 33s);- jar 字节码实证含本次修改:
unzip -p <fat.jar> BOOT-INF/lib/<模块>*.jar核对 .class; start-app.sh重启 → 验 health 200 / 四模板 banner / Flyway up-to-date / admin-api 200(M1 无回归)。
高风险/整机重部署安全变体——隔离验证后再切(2026-06-16 整机改新命名空间
com.wanxiang.huijing实证):步 4 构建出新 jar 后,先用新 jar 在隔离端口(如:48090)+ staging profile 起一个实例,live:48080全程不动;在隔离实例上验全(Flyway 全绿无冲突 / health 200 / 本波新端点逐个 200 code=0 / 免短信登录拿 token)。隔离验全过才切:停老 →start-app.sh起新 → 复验(同上 +smoke-test.sh)。任一步 fail → 停新、用/tmp备份 jar 恢复:48080→ 确认 live 恢复。严禁无隔离验证就切 live;备份 jar 留到复验全过再删。 注:start-app.sh/模块名随命名空间改名已更新(huijing-server),重部署前核对 jar 路径与现命名空间一致。
隔离实例 +
-D注入「真验 feature-flagged 后端改动」(不切 live、零改 repo/yaml)——2026-06-16 Lane E G1 实证(GP9 10/10 真验即用此法):要在 staging 真跑一个默认关闭的 feature-flag 后端改动(验它打开后行为对、关着时现行字节不变),不要改 repo 里的application-staging.yaml、也不要重部署 live。配方:用已部署的 jar(或步 4 新 jar)在隔离端口(如:48090)起一个实例,开关与外部依赖全经 JVM-D注入,例:java -jar <fat.jar> --server.port=48090 --spring.profiles.active=staging \ -Daigc.control-plane.enabled=true \ -Daigc.safety.base-url=http://100.64.0.8:3000 -Daigc.safety.api-key=<key> -Daigc.safety.model=<model>Spring 的
-D/--key=val优先级高于 yaml,故开关与 baseUrl/key/model 临时注入即生效,repo 与 live 实例一行不动。在此隔离实例上跑负例集真验(如 10 条恶意 brief 全被 GP9 拦),验完即停,flag 在主干仍默认关 = 现行字节零变。与上一条「整机重部署安全变体」互补:那条是"新 jar 验全过才切 live",这条是"现行字节不动、只临时验一个 flag 行为"。
教训速记(区分两个隔离用法):同样起
:48090隔离实例——「重部署安全变体」目标是最终要切 live(验全过 → 停老起新);「-D注入真验 flag」目标是永不切 live(只验 flag 行为,验完即停,主干字节不变)。混用会误把"只想验 flag"做成了"切 live"。
后端接 staging 前环境检查:代理劫持防护(创始人 2026-06-22 历史回收判定捡回·边际登记)。staging 机若装了 mihomo 之类代理客户端(占 7890/9090),Java 进程连内网 MySQL/Redis 可能被代理拦截,表现为莫名其妙的连接失败。接线前先把内网网段
100.64.0.0/10加进no_proxy/JVMnonProxyHosts,放行内网中间件连接。(与生成域 new-api 出网调模型的 no_proxy 是两个场景:那条放行出网,这条放行内网。)
边际登记(创始人 2026-06-22 历史回收判定,真部署时补,不在此展开):① 快检部署落点(运维 033)——
deploy/smoke-test.sh等就绪快检在哪台机怎么跑,真部署时在本配方 §5 补;② 观测数据保留期(运维 034)——第一波已定基础保留期(Prometheus/Loki 15 天、Tempo 7 天,见deploy/infra/observability/及 §6);与security-and-reliability.md§6 日志保留期(ERROR/WARN 90 天、INFO 30 天)、磁盘水位告警(运维 021)、¥500/月监控预算(运维 027)的完整对账,待后续波观测真接数据后收口;③ staging 数据脱敏子集归属(运维 006)——真上线后 staging 要不要用生产数据脱敏子集来验(暴露真实数据分布下的 bug),是运维-合规交叉约束,待定(现状 staging 是 seed 假数据,可幂等重建)。
4. 构建/测试门(红线)
vue-tsc --noEmit是假门禁,前端验证一律npm run build。- mini-desktop 构建 game-studio 必须
npm run build -- --mode staging(vite preview 直接 serve 工作树 dist,默认 build = 事实上改部署)。缺--mode staging的实证后果(2026-06-21 bug#4 钉死):.env.staging的VITE_API_BASE不被加载 → axiosbaseURL烤成空串 → 所有/app-api/*打到前端 origin(:4173),而vite preview不代理/app-api(proxy 仅 dev 生效)、SPA fallback 对未知路径回index.html(HTTP 200!)。后果链:getRuntimePackage拿到 HTML 字符串 →request.ts拦截器对「非{code}包络」直接透传 body 不报错 →respVO是字符串 →respVO.manifestUrlundefined → resolvePackage 静默走 democlicker兜底(无 warn)→ demostartRuntime抛「template system under reconstruction」→game_error→ /play 显示「游戏加载失败」。排障锚:CDP 抓包看 package 请求是否打到:48080(应有,且应顺带一条.../manifest请求);grep distcreate({baseURL:应为http://<后端>:48080而非空串。 - huijing 前端(game-admin)稳定配方:pnpm9(
corepack prepare pnpm@9.15.4 --activate,一举绕开 pnpm10+ 的 lockfile 镜像校验与构建脚本审批两道坎)+ 前端目录.npmrc设 npmmirror →node --max_old_space_size=4096 ./node_modules/vite/bin/vite.js build(绕 pnpm-run 预检)。node_modules 腐化(如混入 vite8 / 杂散pnpm-workspace.yaml报 packages 缺失)→ 移开杂散文件 → 清装精确回钉版本。 - huijing-module-system 测试:
SPRING_DATA_REDIS_PORT=26379 mvn test ...(宿主 16379 被 staging redis 占用带密码,嵌入式 RedisServer 失败被吞 → NOAUTH 假红)。 - mini-desktop 长驻 serve/后台进程(2026-06-11 T1-spike 双 lane 实证):ssh 会话内
nohup &/setsid仍可能随会话 teardown 被 SIGHUP 连带杀(症状=稍后访问 ERR_CONNECTION_REFUSED)。稳定配方:首选systemd-run --unit=<name>起 durable 单元;次选独立 launchersetsid bash -c 'exec node serve.cjs'双脱离 + 起服后 5×6s 探活门(curl 200 连续过)确认常驻再继续。另:长任务后台进程严禁与pkill/curl写进同一 heredoc 串行(竞态留孤儿进程占 pid 不占端口)。 - 长任务(真模型 eval / CDP 真玩)焊死前台串行 + 孤儿独立复核(2026-07 实证):这类长任务交给后台代理,它常会再起后台子任务或「起后台等通知」,然后截断返回中间态——mini-desktop/开发机上残留孤儿
python http.server+ headless chrome +/tmpprofile,产物没取回。做法:长任务一律焊死前台串行(禁run_in_background/&/nohup/起后台等通知),每步在 finally 杀 serve + chrome、删 profile。收尾不信代理自报「已清理」,主代理独立 ssh 复核孤儿三项都空才算清:ps aux | grep -E "http.server|chrome" | grep -v grep、lsof -i:<port>查端口占用、ls /tmp查残留 profile。(前台串行纪律与gen-path-parity-harness.md§并发同源;此条补「截断返回中间态」与「独立复核孤儿」两个具体面。)
5. 冒烟门
- API 就绪门:
deploy/smoke-test.sh(只读 12 项)+--deep(13 项含 generate 入队),含 CORS/鉴权(test1 token)验证。 - gotcha:feed item 的
packageUrl恒 null,真实取包走GET /app-api/runtime/package/{versionId}。 - feed 流端点语义(2026-07-05 真机坐实,防 R5 式假警报):主混合流
GET /app-api/feed/stream只收cursor+size、无 zoneId 参,恒服务 zone0;带 zoneId 的专区流是另一个端点GET /app-api/feed/zone/stream?zoneId=N。给/feed/stream传 zoneId 会被静默忽略(曾据此误判「新游戏不在流」)。size有@Max(=20,超限校验失败返空data而非报错——排「流空」先核 size)。发布编排(创始人「汇入主混合流」)对每次发布双写 zone0 主流基线 + 若创作者选非零专区再补写该专区(PublishOrchestrationServiceImpl.writePublishBaseline),故一款游戏可同时现于主流与其专区;新游戏 sort_score=0,排在已回灌的高分游戏之后、未刷分组之首。 - API 全绿 ≠ UI 通:编排器旁路会掩盖 UI 缺陷,用户可见波次收口前必须做一次真 UI 走查(见
ui-walkthrough-cdp.md)。
游戏内容 OSS 单款隔离验收
这条配方只验证“Agent 从 OSS 续改并回存、Runtime 从 OSS 加载、浏览器可玩”,不切 live,也不代表存量游戏完成迁移。
- 在
:48090起隔离后端前先加载/root/game-staging/infra/.env的真实 staging MySQL/Redis 凭据;显式打开aigc.game-content-control.enabled=true、注入凭据档中的 HMAC secret,并设置game.artifact-storage.enforce-committed=true、read-mode=OSS。漏加载 env 会以 MySQL 拒绝访问表现,不能误判为业务代码故障。 - Agent 用
tier2/gen-worker/scripts/game_content_repository.py调prepare取得后端分配的版本身份,再从game-sourcescheckout。LittleJS/Canvas 源码依赖 Git 中的平台引擎与插件,检出目标必须是被.gitignore排除的game-runtime/games/<工作目录>;测试和构建都在这里运行,具体游戏源码与dist仍不得加入 Git。 - 修改完成后只能走
commit:源码归档 → 运行包/素材 → V34 pending → 后端finalize在同一本地事务中写 Runtime 元数据、CAS V34 committed、绑定版本摘要 → Agent 再幂等确认 committed。finalize不修改game_project.current_version_id;任一步失败都不能切走当前可用版本,重试必须幂等;不得直写 project/runtime 表。 - 用
/gstack按新versionId真开预览页并进入游戏。收口证据至少包括:菜单与玩法截图、canvas 非空像素、游戏状态随时间推进、manifest 与声明素材均走/app-api/runtime/package/{versionId}/...、网络中没有 MinIO 直连。截图和结构化 verdict 上传game-evidence,不进 Git。 - 只有浏览器验收通过后才调用
activate --expected-current-version-id <验收前 current>。后端在 Project 锁内比较 current:同一版本重试幂等;并发旧快照或向旧版本回切都拒绝。激活失败时 current 必须保持原值。 gameId、旧versionId和调用租户必须一致。历史 tenant=0 探针不能在 tenant=1 下复用;控制面拒绝是正确安全行为,不得靠改库绕过。- 前端构建必须显式指定隔离 API:
VITE_API_BASE=http://100.64.0.7:48090 npm run build -- --mode staging。iframe 只授予allow-scripts;游戏持久化当前会回落内存适配器,禁止为消除 warning 恢复allow-same-origin。 - Runtime reader 只能
GetObject game-artifacts/tenants/*。验收时同时验证 List、Put、Delete 和game-evidenceGET 被拒绝;不得让 Runtime 复用 writer 或 root 身份。
验收完成后删除临时 OAuth token、对应 Redis 精确 key、本地 checkout 和临时脚本。保留 OSS 中的不可变失败版本用于审计,项目当前版本只指向最后一个通过验收并显式激活的版本。
6. docker compose 部署到 mini-infra(观测栈第一波实战蒸馏,2026-07-05)
观测栈五件(Collector/Prometheus/Tempo/Loki/Grafana)起在 mini-infra 时,从 compose 写好到五件全绿踩了四个坑,没一个是 compose 语法错——docker compose config 全过、pull 也成功,问题全在环境和镜像。起容器前按这四条自查,能省一整轮排障。
- 端口要
ss -tlnp查、别只信docker ps。docker ps --format只列 bridge 网容器的端口映射,host 网容器(mini-infra 的 nacos/rocketmq 用 host 网)和宿主进程占的端口一个都不显示。观测栈 Prometheus 默认 9090,恰好撞上 mini-infra 自己跑的 mihomo(clash 内核,§3 已记它占 9090 当 API 端口)——摸底时docker ps看不到、起容器才报address already in use。起容器前对每个要对外映射的端口跑ss -tlnp | grep :<port>,撞了就让(Prometheus 让到了 9091)。 - 开了
-config.expand-env的组件,配置注释里别写${...}。Loki、Tempo、OTel Collector 开-config.expand-env后,会把整个配置文件文本(连注释一起)拿去展开${VAR},而且校验严格:变量名里出现通配符(比如注释里图省事写${MINIO_*}指代两个变量)就报missing closing brace、容器起不来。注释写成纯文字(「MINIO_ACCESS_KEY / MINIO_SECRET_KEY 两个变量」),别用${}形式。这坑 Tempo 和 Loki 同款,别以为哪个展开器宽松(我第一次还误判 Tempo 宽松、又在修复注释里写了一遍反例${MINIO_*}二次踩坑)。 pull成功不等于 binary 在,起容器前docker run --rm <img> --version验一下。daocloud mirror(mini-infra 的 registry-mirror)缓存的otel/opentelemetry-collector-contrib:0.116.0是坏的——只有两层、缺主 binary,起容器报exec /otelcol-contrib: no such file or directory;docker rmi -f后重新下载 layer、digest 不变、还是坏。这种坏docker compose config和pull都发现不了。验法:docker run --rm <img> --version,输出版本号才算真好。坏了换邻近 tag(0.116.0 坏、0.114.0 和 0.155.0 好;0.115.0 mirror 直接 403)。- 换镜像 tag 要顺带复核配置字段兼容。上一条换 tag 时踩到:降到 collector 0.114.0 后,redaction 处理器不认
blocked_key_patterns/redact_all_types(这俩是新版才加的字段),而脱敏是安全红线、砍了键名屏蔽不行,只能往上取 0.155.0(字段最全、向后兼容 0.116 配置)。换 tag 别只看 binary 起不起来,还要看这版本认不认你的配置字段。
部署落点与运维:compose 源在仓 deploy/infra/observability/、部署位置 mini-infra:/opt/infra-observability/,rsync 同步(.env 排除、服务器上 chmod 600 不入仓),docker compose up -d,HOST=100.64.0.8 bash health-check.sh 五件全 200 = 绿。访问方式(Grafana 口令/端口/MinIO 桶)见 docs/内网凭据与端点.md 观测栈段。ssh 连法同 §2:ssh -o ProxyCommand=none root@100.64.0.8。
7. 生成线(cheap / tier2 Python service)在 mini-desktop 的环境地图(2026-07-05 波③ OTLP 真跑蒸馏,免重复探索)
生成线的 Python 服务(便宜档 cheap-worker、tier2 gen-worker)跑在 mini-desktop,布局与 huijing 后端两码事,摸底时几处岔路很吃时间。下面这些坐标都经波③ OTLP 真跑坐实,后续接生成线直接照用,别再从零翻找。
- 真检出 =
/root/games-development-ai/(dev/2.0.0),不是/root/tier2-run/。后者是一份陈旧检出(停在c0135926、阶段二那批),里头同样有 cheap-worker/ 和 tier2/ 目录、极易误认,但它的.venv早已不在、跑不起来。认准/root/games-development-ai/这份。 - cheap-worker 用自己的 venv:
/root/games-development-ai/cheap-worker/.venv/bin/python(uv 建,装了 agentscope + opentelemetry 的 SDK 与 OTLP/HTTP exporter);tier2 gen-worker 的 venv 在/root/.venvs/agentscope-tier2/;uv本体在/root/.local/bin/uv。系统默认/usr/bin/python3(3.12)里 pip、agentscope、opentelemetry 一个都没有,别拿它跑生成线脚本。 - 这份检出的 cheap-worker/ 带本地未提交改动,别盲
git pull/reset --hard覆盖。mini-desktop 上cheap_service_app.py/cheap_studio.py/cheap_service_driver.py/cheap_verify.py有本地补丁(含m3_stream_patch.py的 M3 流式补丁),处于分叉状态。更新单个文件用scp -o ProxyCommand=none <file> root@100.64.0.7:<path>(单文件传输分类器不拦,见 §2,波③ 部署cheap_otlp_sink.py即用此法),或先git stash核对再动;直接覆盖会抹掉这些没进仓的补丁。 - 生成线端口:便宜档 AgentScope Service :8300、便宜档 worker HTTP :9501、tier2 gen-worker :8200。用上面的 venv python 跑对应入口(
cheap_service_app.py/worker_service.py)起服务,门参与预算从 Nacos(100.64.0.8:8848、namespace 为空串)热读。 - Python 侧跨机 / 出网必须绕本机 clash fake-ip(198.18.x):发往内网(100.64.0.8 的 Collector :4318、new-api :3000 等)前设
NO_PROXY=100.64.0.8,localhost,127.0.0.1并清http_proxy/https_proxy(curl 用--noproxy '*')。便宜档 OTLP sink 已在建 exporter 前调worker.client.install_proxy_bypass幂等兜一层。跨机可达实证:mini-desktop → mini-infra:4318 返 200。 - 波③ OTLP span 真跑配方:设
CHEAP_OTLP_ENDPOINT=http://100.64.0.8:4318,用 cheap venv python 跑一局便宜档生成(或直接把 TraceStep 喂给cheap_otlp_sink.make_cheap_otlp_sink),span 会以service.name=cheap-gen-worker落 Tempo。查 Tempo 要等 ≥15s(Collector tail_samplingdecision_wait=10s叠 Tempo ingest):curl -s --noproxy '*' -G http://100.64.0.8:3200/api/search --data-urlencode 'q={ resource.service.name="cheap-gen-worker" }'。注意采样口径——只有 error 状态的 span 必被采(tail_sampling status_code 策略),正常 span 走 10% 概率采样、查不到是正常现象,真跑验证要用带 error verdict 的 step 才稳。 - 观测栈自身还没被观测:Prometheus 暂未 scrape Collector 的内部 telemetry(容器内 :8888),
otelcol_receiver_accepted_spans_total一类管道计数器在 Prometheus 里查不到(留波⑤ 补 collector self-scrape)。要判断 span 到没到,直接查 Tempo,别指望这些计数器。 - tier2 源工程落库接后端(W-ASSET-SRC T1 接线,2026-07-05):让 tier2 富游戏的 src/ 产物从落本地盘改落 MySQL+MinIO 的开关 = tier2 gen-worker 起服时的环境变量
TIER2_STORE=backend(worker/store.py的default_store()唯一读它,不设=回落LocalFsStore本地盘)。它落在 tier2 服务起栈 env 单点(与上面 :8200 venv 入口同处设,别在别处硬编码BackendStore类)。翻开关前先核两件底账,免一开就撞空库:① tier2 逻辑库要先在真 MySQL 建好,CREATE DATABASE IF NOT EXISTS tier2(对齐infra.yamlmysql.database=tier2);②tier2_source_project_version表的 status 列状态要确认——BackendStore._ensure_schema首次 save 会CREATE TABLE IF NOT EXISTS并按information_schema探测缺列补跑ALTER ADD COLUMN status,但若历史窗口已把这张表建成无 status 列的旧形态,确认它被 ALTER 补上(或确认无存量数据时 DROP 重建),处置记进接线日志。翻开关后先真跑一次「生成一款富游戏 → 收口落库」,确认日志出现BackendStore.save 落库成功(committed)而非LocalFsStore的落库行。回滚 = 起服 env 去掉该开关即整体回落LocalFsStore,一处可逆、无数据迁移。取回重建的五条闭环由scripts/store_roundtrip_harness.py --backend在此真基建上验(本机 worktree 无真库 / 无 esbuild,跑不了 bundle 字节相等,属本窗口 follow-up)。 - tier2 孤儿清扫 job(W-ASSET-SRC T4):manifest-first 把半落库残片变成可见的 pending 行后,超时未收尾的 pending 行连同其 MinIO 前缀对象由
scripts/store_cleanup_job.py回收(乐观删 CAS 防与续 put 的 TOCTOU 竞态、幂等)。在此机用 tier2 同套 venv +TIER2_STORE=backend起,cron / systemd-timer 定时跑即可(如每小时--ttl 3600);owner = 生成线(tier2)。回收正确性已由tests/test_store_backend_manifest_first.py的内存 fake 用例坐实,真库真跑排本窗口。
8. 后端 → 生成线派发接线(executor·内测 dev 闭环命脉,2026-07-05 真 e2e 坐实)
huijing 后端把生成请求投给 Python 生成线,靠 aigc.executor 这组配置——它是内测核心闭环的命脉,却默认关闭。aigc.executor.enabled 缺省 false,起栈时一旦漏注 AIGC_EXECUTOR_*,executor 整组 Bean(WorkerDispatchClient / GenTaskConsumer / @Scheduled tick)被 @ConditionalOnProperty 挡在装配外,生成请求入队成功却无人分派、永远跑不起来、且不报错。2026-07-05 起 dev 全栈正是栽在这:只注了 MYSQL/REDIS/NACOS 三口令、漏了 executor,便宜档 create→generate 静默卡在 queued。
派发链路:submitGenerate 落库 queued → GenTaskProducer 发 RocketMQ → GenTaskConsumer CAS 认领 → WorkerDispatchClient POST job 到 worker-url → Python worker 生成 → 回调 /dify/callback-internal → 落版本 + 运行包;@Scheduled tick 兜底轮询 DB。
dev 正确配置固化在 /root/huijing-dev.env(chmod 600、单一事实源),/root/restart-huijing-dev.sh 从它 systemd-run 重建 transient unit huijing-dev。关键项:AIGC_EXECUTOR_ENABLED=true(装配整组,默认 false = 不分派)、AIGC_WORKER_URL=http://127.0.0.1:9501/generate(逐字节对齐 worker 的 DEFAULT_PORT=9501 + POST /generate)、AIGC_CALLBACK_SECRET 非空 32hex(Complete A 起已配、值见凭据档;worker 起服带 --callback-secret <同值>,两端逐字相同 = HMAC 验签开;空=验签关=内网可伪造回调,内测必须非空)、AIGC_NEWAPI_QUOTA_ENABLED=true(WU2 预置池 per-user 计费)、NEWAPI_KEY = api-key(值见凭据档,全局兜底/MiniMax 计费);dispatcher 默认 http(= WorkerDispatchClient,别开 saa 进程内旧路)、dispatchTemplateId 默认 generic(见下)。
分派闸是硬相等 templateId=='generic'(AigcGenerateExecutor.java:535-536):只有 templateId 恰等 generic 才投 worker,其余(business-sim 等旧模板名)一律 fall-through 到进程内 LLM 旧路、到不了 :9501。便宜档真实流(一句话、不选模板)= 草稿空 templateId → 后端归一 generic → 派 worker,品类由 Python 层按 brief 文本判。冒烟 / e2e 必须用 generic,别用 business-sim——它虽在 SUPPORTED_TEMPLATE_IDS 提交校验放行,却与分派闸解耦、会静默走旧路。
陈旧 /root/game-staging/infra/.env 别照搬:那套 AIGC_* 是 gamedef 时代的值(:9401 / dispatcher=saa / sourcemode=gamedef / gameruntimedir 指退役目录),与 M3 cutover 后的便宜档 Python 线不符,照搬即错。
交付边界:生成成功落的是 status=0 预览包(如 versionId 93151、bundle 243KB),feed 不显示;需运营在审核台 APPROVE(status 0→1)才进 feed。这是生成线与产品 / 审核轨的交界(cutover plan §2.2),不是 bug。可逆:把 /root/huijing-dev.env 里 AIGC_EXECUTOR_ENABLED 改回 false 重跑 restart 脚本,即回到「入队不分派」的现行行为,一处开关、无数据迁移。
9. OTel Java agent 挂载(阶段四观测·面一b,2026-07-05 dev 真机坐实)
给 huijing 后端挂 OpenTelemetry Java agent = 一个参数拿全链 trace:Spring MVC/JDBC/Redis/出网 HTTP 自动织入 span,且出网时自动注入 traceparent(顺带兑现面四的 Java→worker 那跳)。dev 落法不改 restart 脚本硬编码的 java 行,靠 JAVA_TOOL_OPTIONS 注入:
- agent jar =
/root/otel/opentelemetry-javaagent.jar(2.29.0,24MB)。外网从 aliyun maven 镜像--noproxy '*'直连下:maven.aliyun.com/repository/public/io/opentelemetry/javaagent/opentelemetry-javaagent/2.29.0/opentelemetry-javaagent-2.29.0.jar(dev 的~/.m2/settings.xml镜像 central→aliyun,直连可达)。 /root/huijing-dev.env追加一块(单一事实源,可逆=删块重启即回退):JAVA_TOOL_OPTIONS=-javaagent:/root/otel/opentelemetry-javaagent.jar+OTEL_SERVICE_NAME=huijing-server+OTEL_EXPORTER_OTLP_ENDPOINT=http://100.64.0.8:4318(mini-infra Collector,在 no_proxy 内直连)+OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf+OTEL_METRICS_EXPORTER=none+OTEL_LOGS_EXPORTER=none(metrics 归面五 micrometer/prometheus、logs 后置,agent 只发 traces 免重复)+OTEL_TRACES_SAMPLER=parentbased_always_on(dev 全采,尾采样交 Collector)。- 验:重启后启动日志
Picked up JAVA_TOOL_OPTIONS+opentelemetry-javaagent - version: 2.29.0;启动加约 10s(织入开销,可接受);发几个请求后 TempoGET /api/search/tag/service.name/values含huijing-server、GET /api/search?tags=service.name%3Dhuijing-server出真 trace(PING/POST span)。旁路风险兜底:只读冒烟 12/12 全过、agent 无 OTLP 导出错误。
坑与注记:① 指标是进程内 micrometer,重启即清零——Prometheus 抓的时序在 restart 处有 counter reset,rate()/increase() 天然处理;单次 scrape 看不到累计值不是 bug(排「gen_task_total 缺失」先想是不是刚重启过)。② watchdog 超时收尸(stale RUNNING > 15min→failed(timeout))确经 handleCallback 唯一写入路径→面五埋点会计 timed_out(dev 实证 task206 走此路);迟到的重复 worker 回调在 handleCallbackTx 抛 AIGC_CALLBACK_TASK_FINAL 拒绝重入、先于 postCommitBestEffort,故不重计。③ 便宜档需续修的 gen 延迟可达 20min(task206:初生成 + H_progress 门未过续修 + 等远程 LLM → 最终 15min watchdog 阈值收尸 failed(timeout));gen_duration 埋点正为量化这条内测 UX 关注线。