Skip to content

函数注册 [UI 生成链路审核(2026-09] ​

状态说明:本文档为审核落盘 + 修复进度跟踪。所有 file:line 已逐条人工核对(2026-09-19)。 「修复批次」列:B1=本批修复,backlog=记录待办不修。中断后按状态列续作。

0. 链路全景 ​

游戏服 SDK ──TCP:19091──▶ Agent(本地登记 agentlocal) ──TCP:19090──▶ Server ControlService(校验+物化)
                                                                        │
                                            内存 registry ──┬── agent_sessions 表(meta)
                                                            ├── FunctionContract 表(权威 UI 事实源)
                                                            └── PageProposal(页面提案,UI 生成入口)
Web ──HTTP:18780──▶ /api/v1/functions/descriptors(FunctionSpec wire,全 lowerCamel)
  ├─【调试通道】Invoke 工作台:derivePresentationSpec(x-ui-* hints) → SchemaFormRenderer(@rjsf/antd+ajv8) → invoke
  └─【发布通道】服务端 generator 同源推导 → PageProposal → accept-and-publish → PublishedPageSpec → PageRenderer → binding/execute

设计事实:注册契约刻意不含 UI 字段(proto/croupier/agent/v1/register.proto 注释);表单唯一来源是 inputSchema 内受控的 x-* hints(docs/architecture/presentation-hints.md);CLAUDE.md 所称「pack .tgz 携带 UI plugins 远程加载」不存在于代码(全仓无 tar/UI plugin),自定义 UI 已收敛为纯 spec + 受控 widget 枚举。

1. 阶段一:注册链路(后端) ​

#环节关键点卡点(已核对)批次状态
1.1注册错误黑洞 ✅已修internal/server/tcp_listener.go:364-369 吞掉 handleRegisterRequest 全部错误(含契约物化失败),仍回成功agent 以为注册成功;registry 无函数表 → 调用端只见 "no live agent"。且 internal/app/agent/upstream.go syncOnce 完全不读 resp.Warnings(proto 有 warnings 字段,register.proto RegisterResponse),成功路径的校验告警也被静默丢弃B1done
1.2deprecated 断链 ✅已修agentlocal/store.go:153 已保留 Deprecated;proto agentv1.FunctionDescriptor.deprecated=28upstream.go:505-538 syncOnce 组装时不拷贝 meta.Deprecated → server control_handler.go:522 附近 FunctionMeta 构造读 f.GetDeprecated()(547 行仅 openapi 路径)→ 契约表 deprecated 恒 falseB1done
1.3timeoutMs 漏投影 ✅已修模型 function_contract.go:27 TimeoutMs 存在,迁移 0016 已配(svc/migrations.go:253,MinimumRequiredVersion=28)internal/service/contract_projection.go:54-78 FunctionSpecFromContract 不设 TimeoutMs → /functions/descriptors 的 timeoutMs 恒空(执行层直读 DB 不受影响,仅 UI 侧不可见)。同批补 backfillInputFromClassification 的 TimeoutMs 保留(SDK 重注册不带该字段,此前会把 OpenAPI 已声明预算清零——实施中新发现的姊妹缺口)B1done
1.4policy risk 键错位 ✅已修internal/platform/openapi/converter.go:58 写 extension x-riskinternal/api/function/helpers.go:1443 enforceFunctionPolicy 读 x-risk-level → 永不命中,所有函数策略 risk 静默默认 medium(helpers.go:1440);且词表不同(契约 safe/warning/high/danger ↔ policy low/medium/high/danger),需映射B1done
1.5semver 硬门槛(部分处理)control_handler.go:1113-1117 版本非三段 semver 整函数丢弃(仅 warning)OpenAPI provider 路径已修(2026-09-19):config.version 可配 + 解析链 x-version > config.version > info.version > 1.0.0(versionutil 归一,非法值告警回退不丢函数);server 侧 OpenAPI 绑定物化同步归一。扩展注册用 item.ReleaseVersion(app/agent/app.go:562)仍直传,非 semver 发布号函数消失backlog(剩余:extension 路径)—
1.6SDK provider scope 校验形同虚设tcp_local_listener.go:267 connect 不校验 game/env;app.go:177-178 onConnect 用 agent scope 回填 metadataserver validateProviderScope(control_handler.go:850)对 SDK provider 恒通过;local_handler.go:763-866 的校验在生产装配下不可达backlog—
1.7静态注册者恒报 mismatchprovider:(provider.go:186)与 extension:(app.go:562)metadata=nil每次注册触发 provider_scope_mismatch 噪音警告backlog—
1.8警告/操作表纯内存platform/registry/store.go:139-141 registrationWarnings、openapiOperations 不落库重启后 /functions/warnings 历史丢失、/functions/:id/openapi 依赖 agent 重注册回填backlog—
1.9FunctionsList 版本字符串比较functions_list_logic.go 与 upstream.go pickVersion 用 > 字符串序"1.9.0" > "1.10.0" 判错backlog—
1.10SDK 滑动版本门槛control_handler.go:955-977 落后 provider 独占函数整体剔除「函数凭空消失」仅内存 warning 可见(1.8 关联)backlog—

2. 阶段二:UI 生成——调试通道(Invoke 工作台) ​

#环节关键点(已核对)批次状态
2.1审批续跑冲突 ✅已修服务端 approve 即自动重放原 payload(internal/api/approval/service.go:225-254 continueApprovedFunction),响应 DTO 带 result/taskId/resultKind/continuation(dto.go:21,84,104);前端 web/src/pages/Functions/Invoke/index.tsx:350-353 轮询只取 {status,reason} 丢弃续跑结果,:568-575 反而提供「重新调用」按钮 → 用户再点即第二次真实副作用(且 invoke 无幂等键通道 functions.ts:237-254)。发布页链路是对的(OperationPageRenderer 展示续跑结果不给重放按钮)→ 两链行为不一致B1done
2.2无错误边界 ✅已修全 web/src 无 ErrorBoundary/componentDidCatch;schema 畸形($ref 缺失、items 非 object)时 RJSF 渲染抛错 = 整页白屏。现有降级只覆盖「schema 缺失」(formState.status:'unavailable' → Alert + JSON tab)。已修:SchemaFormRenderer 内置 ErrorBoundary(Alert 降级+重试按钮;spec 变化经 key 自动复位),一处包裹覆盖全部调用点(调试通道+发布通道五个渲染器)B1done
2.3descriptor 兜底链functions.ts:145-158 inputSchema ?? input ?? descriptor.input 三级兜底;v1({items:[{input,output}]})/v2({functions})双形态并存backlog—
2.4hints 只扫顶层schemaHints.ts:236-242 collectFields 仅顶层 properties;嵌套字段 x-widget/visibleWhen 无效;asEnumOptions(:130) 要求 value 为 string,整数枚举 label 全丢backlog—
2.5ajv draft-07@rjsf/validator-ajv8 按 draft-07 编译,2020-12 关键字(prefixItems/dependentRequired/$dynamicRef)静默忽略;TS dashboard.ts:50 注释却写「draft-07 / 2020-12」;protoc-gen-croupier 产的是 2020-12 schema——生成侧与校验侧版本错配backlog—
2.6治理字段事前无感wire 有 approval/permission/risk/executionState/timeoutMs,TS FunctionDescriptor(functions.ts:14-38)未声明未消费:下拉不标注高危/需审批/unbound,选中 unbound 照发拿非结构化失败backlog(1.3 修复后至少 timeoutMs 可见)—
2.7死字段/孤儿代码TS FunctionInvokeResponse 的 duration/timestamp/error 后端不存在(functions.ts:178-189 vs dto.go:180-196);broadcast 后端有前端无;plugin/registry.tsx+transform.ts 零生产引用;PermissionGuard/useAnyPermission 零引用backlog—

3. 阶段三:UI 生成——发布通道(Proposal [PublishedPageSpec] ​

#环节关键点(已核对)批次状态
3.1FormFieldSpec 三字段静默丢弃 ✅已修类型双侧齐备:Go internal/dashboard/spec/form_presentation.go:79-96(Required/DefaultValue/ValidationRules),TS dashboard.ts:799-805;Page Studio manual-merge 写路径支持发布(service/versioning/service.go:1365-1393);但 SchemaFormRenderer/index.tsx:239-297 applyFieldPresentation 只消费 label/placeholder/description/enumOptions/disabled/widget/remoteOptions/widgetProps——编辑器配置"看起来成功",运行无声消失。已修:mergeFieldValidity(required/validationRules→ajv 关键字,custom 显式告警)+ applySpecDefaults(defaultValue 种入初值,显式值优先);wire 语义补进 pagespec-protocol.mdB1done
3.2提案重建失败静默注册提交后异步重建(registry/store.go:531-569),失败降级 warning;需手动 POST /pages/proposals/rebuild 兜底,无告警面backlog—
3.3破坏性 schema 变更只告警F12 双处(control_handler.go:585-618 内存基线 + contract_service.go:1002-1036 DB 基线);绑定页面在 freshness 校验前继续按旧 PageSpec 渲染 →「注册成功但 UI 字段对不上」backlog(依赖产品决策:阻断 or 醒目提示)—
3.4ActionSpec.permission 不消费发布页按钮人人可见,点击才服务端 403;requestErrorConfig.ts:210-228 兜底跳 /403backlog—
3.5审批中心 risk 词表错位pages/Approvals/index.tsx 筛选项给 medium/low(策略引擎词表),SDK 契约 descriptor 是 safe/warning 词表——两个下拉互匹配不上;强确认只判 risk==='high' 不覆盖 danger(默认策略 danger 才是 two_person 最高级,policy/manager.go:127-131)backlog(根治需统一 risk 词表,涉及 policy 引擎+迁移,本批不动)—
3.6widget 声明与实现不齐受控枚举允许 RichText/DateRange,widgetToRjsf(index.tsx:227-236) 无实现 → 静默降级文本框backlog—
3.7远程选项失败静默useRemoteOptions.ts:99-103 403/5xx/网络统一吞成空选项,无权限与无数据不可分辨backlog—

4. 本批修复(B1)清单与验收 ​

  1. 1.1 注册错误显式化:tcp_listener.go 物化失败时把错误写入 RegisterResponse.Warnings;upstream.go syncOnce 读取并 slog.Warn resp.Warnings。不动「回成功」语义(避免破坏 agent 重连风暴),先保证可见性。验收:Go 单测(注册失败→warnings 含错误;成功→透传校验 warnings)。
  2. 1.2 deprecated 链路补全:upstream.go syncOnce 拷贝 meta.Deprecated → desc.Deprecated;确认 control_handler.go FunctionMeta 构造读取 f.GetDeprecated(),未读则补。验收:agentlocal→server FunctionMeta→contract 的传递测试。
  3. 1.3 timeoutMs 投影:contract_projection.go FunctionSpecFromContract 补 TimeoutMs: int(contract.TimeoutMs)。验收:投影单测。
  4. 1.4 policy risk 键错位:helpers.go:1443 改读 x-risk 并做词表映射(safe→low、warning→medium、high→high、danger→danger;未知值回落 medium 保持现状)。验收:单测覆盖映射。
  5. 2.1 审批续跑对齐:web/src/services/console.ts queryApprovalStatus 透出 result/taskId/resultKind/continuation;Invoke/index.tsx approved+continuation 时展示结果(sync→InvocationResponse;task→TaskProgressPanel),移除「重新调用」按钮(改为「查看结果/前往审批单」)。对齐发布页行为。验收:组件测试更新。
  6. 2.2 表单错误边界:新增轻量 SchemaFormErrorBoundary 组件包裹 SchemaFormRenderer 全部调用点,抛错降级为 Alert + 提示切 JSON 模式(发布页给结构化空态)。验收:抛错场景测试。
  7. 3.1 FormFieldSpec 三字段生效:applyFieldPresentation 消费 required(并入 runtime schema.required)、defaultValue(参与初值合成,优先级 spec 覆盖 schema default)、validationRules(required/min/max/pattern 翻译进 schema 的 min/max/pattern + required;custom 忽略并 console.warn)。验收:渲染器单测。

DoD 检查(交付前):go build ./...、go test ./internal/...(至少触及包)、gofmt -l . 空、pnpm --dir web run tsc 0 错误、web 相关 jest 用例、bash scripts/dashboard_vnext_guard.sh(仓库根)、文档同步(presentation-hints.md / ui-generation.md 补「required/defaultValue/validationRules 已生效」与 risk 映射说明;CLAUDE.md pack 表述纠偏)。

4A. B2 立项:函数契约版本历史 [已实施(2026-09-19,落地与设计一致,差异见交付记录] ​

现状缺口:function_contracts 每函数仅一行(唯一索引 (game_id,env,function_id)),历史只有 prev_*_schema 一步回看;CapabilitySemanticVersion 是资源语义的版本表先例,函数契约无对应物。

设计(已与用户对齐):

  • 以函数 id 为主维度,每次内容变化(source_digest 比对,重复注册相同内容不写)追加一条:新表 function_contract_versions:(game_id, env, function_id, seq, version, source, source_digest, snapshot_json, change_type created|updated|removed, diff_json, breaking, actor, created_at)。
  • 写路径:rebuildContract / RemoveFunctionContract 与契约写同事务落 history(多库模式随既有持久操作记录+补偿路径)。
  • API:GET /functions/:id/versions(分页列表)、GET /functions/:id/versions/:seq(快照)、GET /functions/:id/versions/diff?from=&to=(字段级 diff,复用 internal/function/schemadiff)。
  • UI:函数详情页「变更历史」tab。
  • 保留策略(用户已定):每函数上限 N 条(实现为常量 50:FunctionContractVersionRetention,同事务内裁剪),超限按最老淘汰。
  • 实现差异(相对草案):① seq 不设唯一索引(多实例 HA 并发注册下唯一冲突会毒化注册事务;读取按 seq,id 稳定排序);② 历史写失败降级为告警不阻断注册(与提案/模板重建同档的衍生数据先例;表存在性由启动期 MinimumRequiredVersion=29 兜底);③ diff 端点按两版快照现算,不拼接链上 diff。
  • 迁移:goose 0029(MinimumRequiredVersion 28→29)+ 三处同步(svc/migrations.go、db/migrate/migrate.go、migrate_test.go 合成清单)+ DropTable 模拟老库回归用例(CLAUDE.md 迁移契约)。

5B. B2 交付记录(2026-09-19) ​

  • 新增:internal/model/function_contract_version.go(表+数据访问:AppendVersion/seq/裁剪/分页/单查)、internal/service/contract_history.go(判等/快照/diff/breaking 计算)、internal/logic/function/contract_versions_logic.go + internal/api/function/contract_versions_handler.go(三端点,scoped 组注册 routes.go)、迁移 0029 三处同步 + TestGoMigrations_ContractVersionsCatchUp 老库回归。
  • 写路径:rebuildContract(created/updated,判据=model.ContractSemanticallyEqual 同一函数)与 RemoveFunctionContract(removed+删除前快照)挂钩;model.GameModels() 注册新表。
  • Web:services/api/functions.ts 三函数(未知 changeType 透传不静默改写)、函数详情页新增「变更历史」tab(VersionsTab:列表/快照 Drawer/两版对比含 breaking 告警)、locale zh/en 全键。
  • 测试:service 3 组(生命周期含去重/breaking/removed、保留裁剪 60→50、列表瘦身)、logic 5 组(分页/详情/diff/scope 隔离/wire key)、jest service 5 用例 + VersionsTab 5 用例。
  • 文档:dashboard-page-model.md(FunctionContractVersion 模型节 + prev 列「无版本表」表述修正)、docs/api/function.md §22-24。

5. 交付记录(B1,2026-09-19) ​

  • Go:go build ./... 通过;go test ./internal/... 全绿;gofmt -l 空。新用例:internal/server/tcp_listener_test.go(materialize 失败经 warnings 可见 + 校验 warnings 透传)、internal/app/agent/coverage_v9b_test.go(deprecated 透传)、internal/api/function/coverage_v9_test.go(x-risk 映射 + 契约行兜底/safe 歧义/scope 缺失)、internal/service/contract_projection_test.go(timeoutMs 投影)、internal/service/contract_service_test.go(backfill 保留 timeout)。
  • Web:pnpm --dir web run tsc 0 错误;jest 相关域全绿(SchemaFormRenderer 14 套件/183 用例、Invoke 7 套件/113 用例、PageRenderer+services 56 套件/840 用例)。新用例:field-validity.test.tsx(14)、error-boundary.test.tsx(3)、approvalPolling.test.ts 透传回归、Invoke 审批流 4 例改写/新增。
  • Guard:bash scripts/dashboard_vnext_guard.sh PASSED。
  • 文档:docs/architecture/pagespec-protocol.md 补渲染器生效语义;docs build 见下方状态。
  • 行为变更提示(上线注意):1.4 修复后,契约声明 high/danger 的函数将首次真实命中审批/角色默认策略(此前全量按 medium 静默评估)——属「声明即契约」恢复,但可能让存量声明首次生效;发布前应抽查线上 function_contracts.risk ∈ {high,danger} 行的策略影响面。
  • 未完成项(边界诚实):Docker 构建 + deploy-self-hosted + CI Core 全绿未在本会话执行,不宣称上线;PageSpec 发布链线上 smoke 未跑(B1 改动不改 spec 字段形态)。

6. backlog 汇总(不在本批) ​

semver 硬门槛(1.5)、provider scope 双路径(1.6)、静态注册者噪音(1.7)、warning/openapi 内存态(1.8)、版本字符串比较(1.9)、SDK 门槛剔函数可观测性(1.10)、v1/v2 双形态(2.3)、嵌套 hints(2.4)、ajv draft 错配(2.5)、治理字段事前标注(2.6)、死字段清理(2.7)、提案重建告警(3.2)、破坏性变更 UI 阻断(3.3)、ActionSpec.permission(3.4)、risk 词表根治(3.5)、widget 枚举补实现(3.6)、远程选项报错(3.7)、CLAUDE.md pack 文档纠偏(部分并入本批文档项)。