@stackonward/cli 0.0.1 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (638) hide show
  1. package/dist/index.js +4588 -205
  2. package/dist/index.js.map +1 -1
  3. package/dist/resources/apple/.claude/docs/architecture-template.md +54 -0
  4. package/dist/resources/apple/.claude/docs/architecture.md +52 -0
  5. package/dist/resources/apple/.claude/docs/index.md +13 -0
  6. package/dist/resources/apple/.claude/docs/security-lifecycle.md +57 -0
  7. package/dist/resources/apple/.claude/docs/stack.md +93 -0
  8. package/dist/resources/apple/.claude/docs/verification.md +63 -0
  9. package/dist/resources/apple/AGENTS.md +397 -0
  10. package/dist/resources/apple/CLAUDE.md +397 -0
  11. package/dist/resources/apple/Gemfile +2 -0
  12. package/dist/resources/apple/Gemfile.lock +365 -0
  13. package/dist/resources/apple/_gitignore +14 -0
  14. package/dist/resources/apple/fastlane/Fastfile +123 -0
  15. package/dist/resources/components/nuxt/site-source/app/app.vue +42 -0
  16. package/dist/resources/components/nuxt/site-source/app/assets/css/site-shell.css +1035 -0
  17. package/dist/resources/components/nuxt/site-source/app/assets/icons/brand/.gitkeep +0 -0
  18. package/dist/resources/components/nuxt/site-source/app/assets/icons/common/.gitkeep +0 -0
  19. package/dist/resources/components/nuxt/site-source/app/components/site/ApplicationHeader.vue +35 -0
  20. package/dist/resources/components/nuxt/site-source/app/components/site/ApplicationShell.vue +56 -0
  21. package/dist/resources/components/nuxt/site-source/app/components/site/ApplicationSidebar.vue +40 -0
  22. package/dist/resources/components/nuxt/site-source/app/components/site/BackToTop.vue +42 -0
  23. package/dist/resources/components/nuxt/site-source/app/components/site/ContentArticlePage.vue +40 -0
  24. package/dist/resources/components/nuxt/site-source/app/components/site/ContentListPage.vue +30 -0
  25. package/dist/resources/components/nuxt/site-source/app/components/site/CookieConsent.vue +18 -0
  26. package/dist/resources/components/nuxt/site-source/app/components/site/MarketingFooter.vue +52 -0
  27. package/dist/resources/components/nuxt/site-source/app/components/site/MarketingHeader.vue +106 -0
  28. package/dist/resources/components/nuxt/site-source/app/components/site/MarketingShell.vue +35 -0
  29. package/dist/resources/components/nuxt/site-source/app/components/site/SiteBrand.vue +10 -0
  30. package/dist/resources/components/nuxt/site-source/app/components/site/StaticPage.vue +21 -0
  31. package/dist/resources/components/nuxt/site-source/app/components/site/ToastViewport.vue +25 -0
  32. package/dist/resources/components/nuxt/site-source/app/components/ui/BaseButton.vue +86 -0
  33. package/dist/resources/components/nuxt/site-source/app/components/ui/BaseGradientBorder.vue +59 -0
  34. package/dist/resources/components/nuxt/site-source/app/components/ui/BaseModal.vue +131 -0
  35. package/dist/resources/components/nuxt/site-source/app/components/ui/BasePopover.vue +150 -0
  36. package/dist/resources/components/nuxt/site-source/app/components/ui/BaseText.vue +138 -0
  37. package/dist/resources/components/nuxt/site-source/app/components/ui/CheckoutStatus.vue +112 -0
  38. package/dist/resources/components/nuxt/site-source/app/components/ui/CheckoutSummary.vue +172 -0
  39. package/dist/resources/components/nuxt/site-source/app/components/ui/GoogleSignInButton.vue +113 -0
  40. package/dist/resources/components/nuxt/site-source/app/components/ui/InfiniteScroll.vue +123 -0
  41. package/dist/resources/components/nuxt/site-source/app/components/ui/YoutubeEmbed.vue +126 -0
  42. package/dist/resources/components/nuxt/site-source/app/composables/useAnalytics.ts +24 -0
  43. package/dist/resources/components/nuxt/site-source/app/composables/useCheckout.ts +121 -0
  44. package/dist/resources/components/nuxt/site-source/app/composables/useConsent.ts +31 -0
  45. package/dist/resources/components/nuxt/site-source/app/composables/useGoogleIdentity.ts +163 -0
  46. package/dist/resources/components/nuxt/site-source/app/composables/useNavigation.ts +11 -0
  47. package/dist/resources/components/nuxt/site-source/app/composables/useSeo.ts +33 -0
  48. package/dist/resources/components/nuxt/site-source/app/composables/useSiteShell.ts +58 -0
  49. package/dist/resources/components/nuxt/site-source/app/composables/useToasts.ts +48 -0
  50. package/dist/resources/components/nuxt/site-source/app/layouts/app.vue +5 -0
  51. package/dist/resources/components/nuxt/site-source/app/layouts/default.vue +5 -0
  52. package/dist/resources/components/nuxt/site-source/app/plugins/analytics.client.ts +121 -0
  53. package/dist/resources/components/nuxt/site-source/app/plugins/google-identity.client.ts +24 -0
  54. package/dist/resources/components/nuxt/site-source/app/theme/accordion.ts +9 -0
  55. package/dist/resources/components/nuxt/site-source/app/theme/breadcrumb.ts +8 -0
  56. package/dist/resources/components/nuxt/site-source/app/theme/button.ts +92 -0
  57. package/dist/resources/components/nuxt/site-source/app/theme/carousel.ts +11 -0
  58. package/dist/resources/components/nuxt/site-source/app/theme/checkbox.ts +12 -0
  59. package/dist/resources/components/nuxt/site-source/app/theme/index.ts +30 -0
  60. package/dist/resources/components/nuxt/site-source/app/theme/input.ts +47 -0
  61. package/dist/resources/components/nuxt/site-source/app/theme/modal.ts +9 -0
  62. package/dist/resources/components/nuxt/site-source/app/theme/pagination.ts +6 -0
  63. package/dist/resources/components/nuxt/site-source/app/theme/select.ts +23 -0
  64. package/dist/resources/components/nuxt/site-source/app/theme/stepper.ts +10 -0
  65. package/dist/resources/components/nuxt/site-source/app/theme/toast.ts +8 -0
  66. package/dist/resources/components/nuxt/site-source/app/theme/tooltip.ts +6 -0
  67. package/dist/resources/components/nuxt/site-source/app/types/site-shell.ts +85 -0
  68. package/dist/resources/components/react/ui/Button.tsx +73 -0
  69. package/dist/resources/components/react/ui/Input.tsx +44 -0
  70. package/dist/resources/components/registry.json +355 -0
  71. package/dist/resources/components/vue/feedback/Toast.vue +120 -0
  72. package/dist/resources/components/vue/feedback/useToast.ts +70 -0
  73. package/dist/resources/components/vue/media/FileUpload.vue +229 -0
  74. package/dist/resources/components/vue/media/VideoPlayer.vue +157 -0
  75. package/dist/resources/components/vue/ui/AppImage.vue +147 -0
  76. package/dist/resources/components/vue/ui/BottomSheet.vue +217 -0
  77. package/dist/resources/components/vue/ui/Button.vue +154 -0
  78. package/dist/resources/components/vue/ui/Card.vue +111 -0
  79. package/dist/resources/components/vue/ui/Input.vue +113 -0
  80. package/dist/resources/components/vue/ui/Modal.vue +193 -0
  81. package/dist/resources/components/vue/ui/ScrollArea.vue +163 -0
  82. package/dist/resources/components/vue/ui/Tooltip.vue +115 -0
  83. package/dist/resources/registry.json +158 -0
  84. package/dist/resources/snippets/node/cache.ts +116 -0
  85. package/dist/resources/snippets/node/date.ts +126 -0
  86. package/dist/resources/snippets/node/logger.ts +104 -0
  87. package/dist/resources/snippets/node/validation.ts +132 -0
  88. package/dist/resources/snippets/react/useDebounce.ts +90 -0
  89. package/dist/resources/snippets/registry.json +65 -0
  90. package/dist/resources/snippets/vue/useAsync.ts +96 -0
  91. package/dist/resources/snippets/vue/usePagination.ts +102 -0
  92. package/dist/resources/templates/base/.claude/docs/architecture-template.md +459 -0
  93. package/dist/resources/templates/base/.claude/docs/architecture.md +137 -0
  94. package/dist/resources/templates/base/.claude/docs/index.md +42 -0
  95. package/dist/resources/templates/base/.claude/docs/security-lifecycle.md +129 -0
  96. package/dist/resources/templates/base/.claude/docs/verification.md +121 -0
  97. package/dist/resources/templates/base/AGENTS.md +415 -0
  98. package/dist/resources/templates/base/CLAUDE.md +415 -0
  99. package/dist/resources/templates/go-backend/.claude/docs/api-design.md +486 -0
  100. package/dist/resources/templates/go-backend/.claude/docs/architecture-template.md +449 -0
  101. package/dist/resources/templates/go-backend/.claude/docs/check-list.md +155 -0
  102. package/dist/resources/templates/go-backend/.claude/docs/database.md +369 -0
  103. package/dist/resources/templates/go-backend/.claude/docs/e2e-standards.md +161 -0
  104. package/dist/resources/templates/go-backend/.claude/docs/error-handling.md +558 -0
  105. package/dist/resources/templates/go-backend/.claude/docs/event-runtime-boundary.md +124 -0
  106. package/dist/resources/templates/go-backend/.claude/docs/identifier-contract.md +195 -0
  107. package/dist/resources/templates/go-backend/.claude/docs/index.md +22 -0
  108. package/dist/resources/templates/go-backend/.claude/docs/logging.md +314 -0
  109. package/dist/resources/templates/go-backend/.claude/docs/mediagen-provider-onboarding-sop.md +917 -0
  110. package/dist/resources/templates/go-backend/.claude/docs/mediagen-provider-quick-reference.md +195 -0
  111. package/dist/resources/templates/go-backend/.claude/docs/module-standards.md +309 -0
  112. package/dist/resources/templates/go-backend/.claude/docs/openapi-contract.md +141 -0
  113. package/dist/resources/templates/go-backend/.claude/docs/payment-gateway-onboarding.md +283 -0
  114. package/dist/resources/templates/go-backend/.claude/docs/platform-boundary.md +191 -0
  115. package/dist/resources/templates/go-backend/.claude/docs/quality-gates.md +112 -0
  116. package/dist/resources/templates/go-backend/.dockerignore +6 -0
  117. package/dist/resources/templates/go-backend/.env.example +13 -0
  118. package/dist/resources/templates/go-backend/.gitattributes +2 -0
  119. package/dist/resources/templates/go-backend/AGENTS.md +1139 -0
  120. package/dist/resources/templates/go-backend/CLAUDE.md +1139 -0
  121. package/dist/resources/templates/go-backend/Dockerfile +14 -0
  122. package/dist/resources/templates/go-backend/Makefile +71 -0
  123. package/dist/resources/templates/go-backend/README.md +66 -0
  124. package/dist/resources/templates/go-backend/_gitignore +10 -0
  125. package/dist/resources/templates/go-backend/api/openapi/README.md +29 -0
  126. package/dist/resources/templates/go-backend/api/openapi/dist/artifact-manifest.json +19 -0
  127. package/dist/resources/templates/go-backend/api/openapi/dist/conformance-matrix.json +276 -0
  128. package/dist/resources/templates/go-backend/api/openapi/document.go +30 -0
  129. package/dist/resources/templates/go-backend/api/openapi/package.json +28 -0
  130. package/dist/resources/templates/go-backend/api/openapi/pnpm-lock.yaml +3052 -0
  131. package/dist/resources/templates/go-backend/api/openapi/redocly-source.yaml +18 -0
  132. package/dist/resources/templates/go-backend/api/openapi/redocly.yaml +8 -0
  133. package/dist/resources/templates/go-backend/api/openapi/sdk-generators.json +16 -0
  134. package/dist/resources/templates/go-backend/api/openapi/service-registry.json +39 -0
  135. package/dist/resources/templates/go-backend/api/openapi/services/product-api/conformance/runtime.arazzo.yaml +65 -0
  136. package/dist/resources/templates/go-backend/api/openapi/services/product-api/dist/internal.openapi.json +327 -0
  137. package/dist/resources/templates/go-backend/api/openapi/services/product-api/dist/runtime-contract.json +56 -0
  138. package/dist/resources/templates/go-backend/api/openapi/services/product-api/openapi.yaml +222 -0
  139. package/dist/resources/templates/go-backend/api/openapi/services/worker/conformance/runtime.arazzo.yaml +65 -0
  140. package/dist/resources/templates/go-backend/api/openapi/services/worker/dist/internal.openapi.json +327 -0
  141. package/dist/resources/templates/go-backend/api/openapi/services/worker/dist/runtime-contract.json +56 -0
  142. package/dist/resources/templates/go-backend/api/openapi/services/worker/openapi.yaml +222 -0
  143. package/dist/resources/templates/go-backend/api/openapi/tooling/breaking.mjs +416 -0
  144. package/dist/resources/templates/go-backend/api/openapi/tooling/build.mjs +103 -0
  145. package/dist/resources/templates/go-backend/api/openapi/tooling/conformance_coverage.mjs +111 -0
  146. package/dist/resources/templates/go-backend/api/openapi/tooling/contract_compiler.mjs +62 -0
  147. package/dist/resources/templates/go-backend/api/openapi/tooling/contract_policy.mjs +208 -0
  148. package/dist/resources/templates/go-backend/api/openapi/tooling/delegated_contract.mjs +302 -0
  149. package/dist/resources/templates/go-backend/api/openapi/tooling/deterministic_archive.mjs +69 -0
  150. package/dist/resources/templates/go-backend/api/openapi/tooling/documentation_example_privacy.mjs +93 -0
  151. package/dist/resources/templates/go-backend/api/openapi/tooling/documentation_projection.mjs +498 -0
  152. package/dist/resources/templates/go-backend/api/openapi/tooling/documentation_quality.mjs +346 -0
  153. package/dist/resources/templates/go-backend/api/openapi/tooling/git_artifact_history.mjs +188 -0
  154. package/dist/resources/templates/go-backend/api/openapi/tooling/manifest.mjs +170 -0
  155. package/dist/resources/templates/go-backend/api/openapi/tooling/overlay.mjs +98 -0
  156. package/dist/resources/templates/go-backend/api/openapi/tooling/project.mjs +549 -0
  157. package/dist/resources/templates/go-backend/api/openapi/tooling/route_contract.mjs +156 -0
  158. package/dist/resources/templates/go-backend/api/openapi/tooling/runtime_conformance.mjs +193 -0
  159. package/dist/resources/templates/go-backend/api/openapi/tooling/scalar_asset_localization.mjs +164 -0
  160. package/dist/resources/templates/go-backend/api/openapi/tooling/sdk_generator.mjs +229 -0
  161. package/dist/resources/templates/go-backend/api/openapi/tooling/semantic_version.mjs +64 -0
  162. package/dist/resources/templates/go-backend/api/openapi/tooling/service_contract_inputs.mjs +16 -0
  163. package/dist/resources/templates/go-backend/api/openapi/tooling/test/breaking.test.mjs +365 -0
  164. package/dist/resources/templates/go-backend/api/openapi/tooling/test/build.test.mjs +42 -0
  165. package/dist/resources/templates/go-backend/api/openapi/tooling/test/conformance_coverage.test.mjs +88 -0
  166. package/dist/resources/templates/go-backend/api/openapi/tooling/test/contract_policy.test.mjs +257 -0
  167. package/dist/resources/templates/go-backend/api/openapi/tooling/test/delegated_contract.test.mjs +205 -0
  168. package/dist/resources/templates/go-backend/api/openapi/tooling/test/documentation_example_privacy.test.mjs +51 -0
  169. package/dist/resources/templates/go-backend/api/openapi/tooling/test/documentation_projection.test.mjs +368 -0
  170. package/dist/resources/templates/go-backend/api/openapi/tooling/test/documentation_quality.test.mjs +151 -0
  171. package/dist/resources/templates/go-backend/api/openapi/tooling/test/git_artifact_history.test.mjs +60 -0
  172. package/dist/resources/templates/go-backend/api/openapi/tooling/test/head_policy.test.mjs +8 -0
  173. package/dist/resources/templates/go-backend/api/openapi/tooling/test/overlay.test.mjs +44 -0
  174. package/dist/resources/templates/go-backend/api/openapi/tooling/test/projection.test.mjs +270 -0
  175. package/dist/resources/templates/go-backend/api/openapi/tooling/test/redocly_documentation_rules.test.mjs +146 -0
  176. package/dist/resources/templates/go-backend/api/openapi/tooling/test/release_manifest.test.mjs +76 -0
  177. package/dist/resources/templates/go-backend/api/openapi/tooling/test/route_contract.test.mjs +119 -0
  178. package/dist/resources/templates/go-backend/api/openapi/tooling/test/runtime_conformance.test.mjs +75 -0
  179. package/dist/resources/templates/go-backend/api/openapi/tooling/test/scalar_asset_localization.test.mjs +50 -0
  180. package/dist/resources/templates/go-backend/api/openapi/tooling/test/sdk_generator.test.mjs +31 -0
  181. package/dist/resources/templates/go-backend/api/openapi/tooling/test/semantic_version.test.mjs +60 -0
  182. package/dist/resources/templates/go-backend/api/openapi/tooling/test/service_contract_inputs.test.mjs +27 -0
  183. package/dist/resources/templates/go-backend/cmd/internal/runtime/resources.go +125 -0
  184. package/dist/resources/templates/go-backend/cmd/internal/runtime/runtime.go +102 -0
  185. package/dist/resources/templates/go-backend/cmd/internal/runtime/runtime_test.go +70 -0
  186. package/dist/resources/templates/go-backend/cmd/product-api/lifecycle_test.go +58 -0
  187. package/dist/resources/templates/go-backend/cmd/product-api/main.go +32 -0
  188. package/dist/resources/templates/go-backend/cmd/product-api/wire.go +13 -0
  189. package/dist/resources/templates/go-backend/cmd/product-api/wire_gen.go +94 -0
  190. package/dist/resources/templates/go-backend/cmd/worker/main.go +47 -0
  191. package/dist/resources/templates/go-backend/cmd/worker/wire.go +14 -0
  192. package/dist/resources/templates/go-backend/cmd/worker/wire_gen.go +111 -0
  193. package/dist/resources/templates/go-backend/configs/config.yaml +37 -0
  194. package/dist/resources/templates/go-backend/deployments/compose.yaml +71 -0
  195. package/dist/resources/templates/go-backend/docs/openapi-provenance.json +489 -0
  196. package/dist/resources/templates/go-backend/docs/runtime-provenance.json +175 -0
  197. package/dist/resources/templates/go-backend/docs/source-provenance.json +3568 -0
  198. package/dist/resources/templates/go-backend/docs/standards-provenance.json +1624 -0
  199. package/dist/resources/templates/go-backend/go.mod +256 -0
  200. package/dist/resources/templates/go-backend/go.sum +805 -0
  201. package/dist/resources/templates/go-backend/internal/common/canonicaljson/canonical.go +370 -0
  202. package/dist/resources/templates/go-backend/internal/common/canonicaljson/canonical_test.go +223 -0
  203. package/dist/resources/templates/go-backend/internal/common/checkoutbff/contracts.go +125 -0
  204. package/dist/resources/templates/go-backend/internal/common/checkoutbff/platform_client.go +212 -0
  205. package/dist/resources/templates/go-backend/internal/common/checkoutbff/platform_client_test.go +319 -0
  206. package/dist/resources/templates/go-backend/internal/common/contentaccess/contentaccess.go +50 -0
  207. package/dist/resources/templates/go-backend/internal/common/contracts/contact.go +85 -0
  208. package/dist/resources/templates/go-backend/internal/common/ctxutil/identity.go +20 -0
  209. package/dist/resources/templates/go-backend/internal/common/ctxutil/product.go +53 -0
  210. package/dist/resources/templates/go-backend/internal/common/enumdomain/payment.go +12 -0
  211. package/dist/resources/templates/go-backend/internal/common/enums/dto_admin.go +9 -0
  212. package/dist/resources/templates/go-backend/internal/common/enums/enum.go +1247 -0
  213. package/dist/resources/templates/go-backend/internal/common/enums/handler_admin.go +66 -0
  214. package/dist/resources/templates/go-backend/internal/common/enums/module.go +46 -0
  215. package/dist/resources/templates/go-backend/internal/common/enums/provider.go +10 -0
  216. package/dist/resources/templates/go-backend/internal/common/enums/register.go +11 -0
  217. package/dist/resources/templates/go-backend/internal/common/enums/register_audit.go +274 -0
  218. package/dist/resources/templates/go-backend/internal/common/enums/register_command_replay.go +12 -0
  219. package/dist/resources/templates/go-backend/internal/common/enums/register_config.go +27 -0
  220. package/dist/resources/templates/go-backend/internal/common/enums/register_locale.go +37 -0
  221. package/dist/resources/templates/go-backend/internal/common/enums/register_ops.go +14 -0
  222. package/dist/resources/templates/go-backend/internal/common/enums/register_outbox.go +15 -0
  223. package/dist/resources/templates/go-backend/internal/common/enums/register_rule_expression.go +63 -0
  224. package/dist/resources/templates/go-backend/internal/common/enums/registry.go +77 -0
  225. package/dist/resources/templates/go-backend/internal/common/httporigin/origin.go +62 -0
  226. package/dist/resources/templates/go-backend/internal/common/httporigin/origin_test.go +38 -0
  227. package/dist/resources/templates/go-backend/internal/common/iamcredential/plaintext.go +35 -0
  228. package/dist/resources/templates/go-backend/internal/common/iamcredential/plaintext_test.go +29 -0
  229. package/dist/resources/templates/go-backend/internal/common/iamscope/scope.go +212 -0
  230. package/dist/resources/templates/go-backend/internal/common/iamscope/scope_binding.go +26 -0
  231. package/dist/resources/templates/go-backend/internal/common/iamscope/scope_filter.go +173 -0
  232. package/dist/resources/templates/go-backend/internal/common/iamscope/scope_filter_test.go +279 -0
  233. package/dist/resources/templates/go-backend/internal/common/internalapi/authorization_details.go +10 -0
  234. package/dist/resources/templates/go-backend/internal/common/internalapi/route_permission_policies.go +660 -0
  235. package/dist/resources/templates/go-backend/internal/common/internalapi/route_permission_policies_test.go +331 -0
  236. package/dist/resources/templates/go-backend/internal/common/internalservice/audience_token_source.go +166 -0
  237. package/dist/resources/templates/go-backend/internal/common/internalservice/bootstrap_token_exchanger.go +183 -0
  238. package/dist/resources/templates/go-backend/internal/common/internalservice/bound_service_token_signer.go +52 -0
  239. package/dist/resources/templates/go-backend/internal/common/internalservice/delegated_access_token_exchanger.go +189 -0
  240. package/dist/resources/templates/go-backend/internal/common/internalservice/delegated_access_token_exchanger_test.go +159 -0
  241. package/dist/resources/templates/go-backend/internal/common/internalservice/product_scoped_service_token_signer.go +92 -0
  242. package/dist/resources/templates/go-backend/internal/common/internalservice/provider.go +25 -0
  243. package/dist/resources/templates/go-backend/internal/common/internalservice/request_signer.go +86 -0
  244. package/dist/resources/templates/go-backend/internal/common/internalservice/resolver.go +69 -0
  245. package/dist/resources/templates/go-backend/internal/common/internalservice/resolver_test.go +42 -0
  246. package/dist/resources/templates/go-backend/internal/common/internalservice/route_scope.go +95 -0
  247. package/dist/resources/templates/go-backend/internal/common/internalservice/route_scope_test.go +78 -0
  248. package/dist/resources/templates/go-backend/internal/common/internalservice/service_identity_test.go +387 -0
  249. package/dist/resources/templates/go-backend/internal/common/jsonschema/compiler.go +100 -0
  250. package/dist/resources/templates/go-backend/internal/common/jsonschema/compiler_test.go +41 -0
  251. package/dist/resources/templates/go-backend/internal/common/jwksapi/handler.go +83 -0
  252. package/dist/resources/templates/go-backend/internal/common/jwksapi/handler_test.go +104 -0
  253. package/dist/resources/templates/go-backend/internal/common/locale/locale.go +145 -0
  254. package/dist/resources/templates/go-backend/internal/common/locale/locale_test.go +75 -0
  255. package/dist/resources/templates/go-backend/internal/common/mediaref/mediaref.go +54 -0
  256. package/dist/resources/templates/go-backend/internal/common/mediaref/mediaref_test.go +77 -0
  257. package/dist/resources/templates/go-backend/internal/common/middleware/apikey_auth.go +320 -0
  258. package/dist/resources/templates/go-backend/internal/common/middleware/apikey_throttle.go +158 -0
  259. package/dist/resources/templates/go-backend/internal/common/middleware/auth.go +40 -0
  260. package/dist/resources/templates/go-backend/internal/common/middleware/auth_test.go +50 -0
  261. package/dist/resources/templates/go-backend/internal/common/middleware/client_context.go +86 -0
  262. package/dist/resources/templates/go-backend/internal/common/middleware/cors.go +68 -0
  263. package/dist/resources/templates/go-backend/internal/common/middleware/event_context.go +18 -0
  264. package/dist/resources/templates/go-backend/internal/common/middleware/gateway_identity.go +279 -0
  265. package/dist/resources/templates/go-backend/internal/common/middleware/helpers.go +159 -0
  266. package/dist/resources/templates/go-backend/internal/common/middleware/iam_delegated_route_permission.go +171 -0
  267. package/dist/resources/templates/go-backend/internal/common/middleware/iam_delegated_token_auth.go +151 -0
  268. package/dist/resources/templates/go-backend/internal/common/middleware/iam_delegated_token_auth_test.go +99 -0
  269. package/dist/resources/templates/go-backend/internal/common/middleware/iam_service_identity.go +138 -0
  270. package/dist/resources/templates/go-backend/internal/common/middleware/iam_service_token_auth.go +74 -0
  271. package/dist/resources/templates/go-backend/internal/common/middleware/idempotent.go +273 -0
  272. package/dist/resources/templates/go-backend/internal/common/middleware/idempotent_test.go +83 -0
  273. package/dist/resources/templates/go-backend/internal/common/middleware/internal_route_permission.go +45 -0
  274. package/dist/resources/templates/go-backend/internal/common/middleware/internal_route_permission_test.go +186 -0
  275. package/dist/resources/templates/go-backend/internal/common/middleware/logging.go +191 -0
  276. package/dist/resources/templates/go-backend/internal/common/middleware/metrics.go +45 -0
  277. package/dist/resources/templates/go-backend/internal/common/middleware/middleware_test.go +1516 -0
  278. package/dist/resources/templates/go-backend/internal/common/middleware/principal_context.go +160 -0
  279. package/dist/resources/templates/go-backend/internal/common/middleware/principal_context_test.go +355 -0
  280. package/dist/resources/templates/go-backend/internal/common/middleware/product_context.go +100 -0
  281. package/dist/resources/templates/go-backend/internal/common/middleware/recovery.go +40 -0
  282. package/dist/resources/templates/go-backend/internal/common/middleware/referral_attribution.go +178 -0
  283. package/dist/resources/templates/go-backend/internal/common/middleware/requestid.go +48 -0
  284. package/dist/resources/templates/go-backend/internal/common/middleware/resource_permission.go +790 -0
  285. package/dist/resources/templates/go-backend/internal/common/middleware/resource_permission_test.go +440 -0
  286. package/dist/resources/templates/go-backend/internal/common/middleware/service_credential_auth.go +67 -0
  287. package/dist/resources/templates/go-backend/internal/common/middleware/tenant.go +148 -0
  288. package/dist/resources/templates/go-backend/internal/common/middleware/throttle.go +310 -0
  289. package/dist/resources/templates/go-backend/internal/common/middleware/trusted_client_ip.go +23 -0
  290. package/dist/resources/templates/go-backend/internal/common/middleware/trusted_client_ip_test.go +60 -0
  291. package/dist/resources/templates/go-backend/internal/common/money/currency.go +25 -0
  292. package/dist/resources/templates/go-backend/internal/common/money/errors.go +23 -0
  293. package/dist/resources/templates/go-backend/internal/common/money/format.go +123 -0
  294. package/dist/resources/templates/go-backend/internal/common/money/format_test.go +204 -0
  295. package/dist/resources/templates/go-backend/internal/common/money/gateway.go +144 -0
  296. package/dist/resources/templates/go-backend/internal/common/money/gateway_test.go +165 -0
  297. package/dist/resources/templates/go-backend/internal/common/money/rounding.go +56 -0
  298. package/dist/resources/templates/go-backend/internal/common/money/rounding_test.go +51 -0
  299. package/dist/resources/templates/go-backend/internal/common/principal/context.go +56 -0
  300. package/dist/resources/templates/go-backend/internal/common/principal/principal.go +49 -0
  301. package/dist/resources/templates/go-backend/internal/common/productcode/code.go +24 -0
  302. package/dist/resources/templates/go-backend/internal/common/productcode/code_test.go +35 -0
  303. package/dist/resources/templates/go-backend/internal/common/providerconfig/schema.go +577 -0
  304. package/dist/resources/templates/go-backend/internal/common/providerconfig/schema_test.go +47 -0
  305. package/dist/resources/templates/go-backend/internal/common/requestprivacy/request.go +246 -0
  306. package/dist/resources/templates/go-backend/internal/common/requestprivacy/request_test.go +170 -0
  307. package/dist/resources/templates/go-backend/internal/common/response/pagination.go +60 -0
  308. package/dist/resources/templates/go-backend/internal/common/response/response.go +110 -0
  309. package/dist/resources/templates/go-backend/internal/common/response/response_test.go +463 -0
  310. package/dist/resources/templates/go-backend/internal/common/ruleexpression/ast.go +141 -0
  311. package/dist/resources/templates/go-backend/internal/common/ruleexpression/ast_test.go +81 -0
  312. package/dist/resources/templates/go-backend/internal/common/ruleexpression/canonical.go +184 -0
  313. package/dist/resources/templates/go-backend/internal/common/ruleexpression/canonical_test.go +319 -0
  314. package/dist/resources/templates/go-backend/internal/common/ruleexpression/decoder.go +555 -0
  315. package/dist/resources/templates/go-backend/internal/common/ruleexpression/decoder_test.go +260 -0
  316. package/dist/resources/templates/go-backend/internal/common/ruleexpression/errors.go +55 -0
  317. package/dist/resources/templates/go-backend/internal/common/ruleexpression/evaluator.go +96 -0
  318. package/dist/resources/templates/go-backend/internal/common/ruleexpression/evaluator_test.go +220 -0
  319. package/dist/resources/templates/go-backend/internal/common/ruleexpression/fuzz_test.go +116 -0
  320. package/dist/resources/templates/go-backend/internal/common/ruleexpression/limits.go +331 -0
  321. package/dist/resources/templates/go-backend/internal/common/ruleexpression/schema.go +412 -0
  322. package/dist/resources/templates/go-backend/internal/common/ruleexpression/schema_test.go +164 -0
  323. package/dist/resources/templates/go-backend/internal/common/runtimeinput/runtime_input.go +627 -0
  324. package/dist/resources/templates/go-backend/internal/common/runtimeinput/runtime_input_test.go +245 -0
  325. package/dist/resources/templates/go-backend/internal/common/semver/errors.go +6 -0
  326. package/dist/resources/templates/go-backend/internal/common/semver/semver.go +125 -0
  327. package/dist/resources/templates/go-backend/internal/common/semver/semver_test.go +205 -0
  328. package/dist/resources/templates/go-backend/internal/common/serviceidentity/audience.go +48 -0
  329. package/dist/resources/templates/go-backend/internal/common/serviceidentity/audience_test.go +35 -0
  330. package/dist/resources/templates/go-backend/internal/common/serviceidentity/resource_identifier.go +33 -0
  331. package/dist/resources/templates/go-backend/internal/common/serviceidentity/resource_identifier_test.go +32 -0
  332. package/dist/resources/templates/go-backend/internal/common/tradeno/generator.go +99 -0
  333. package/dist/resources/templates/go-backend/internal/common/tradeno/generator_test.go +396 -0
  334. package/dist/resources/templates/go-backend/internal/common/tradeno/prefix.go +247 -0
  335. package/dist/resources/templates/go-backend/internal/common/types/asset_url.go +11 -0
  336. package/dist/resources/templates/go-backend/internal/common/types/billing.go +89 -0
  337. package/dist/resources/templates/go-backend/internal/common/types/billing_test.go +77 -0
  338. package/dist/resources/templates/go-backend/internal/common/types/country.go +74 -0
  339. package/dist/resources/templates/go-backend/internal/common/types/country_test.go +28 -0
  340. package/dist/resources/templates/go-backend/internal/common/types/currency.go +60 -0
  341. package/dist/resources/templates/go-backend/internal/common/types/identifiers.go +41 -0
  342. package/dist/resources/templates/go-backend/internal/common/types/operator.go +28 -0
  343. package/dist/resources/templates/go-backend/internal/common/types/subscription_entitlement.go +24 -0
  344. package/dist/resources/templates/go-backend/internal/common/types/subscription_entitlement_test.go +35 -0
  345. package/dist/resources/templates/go-backend/internal/common/types/tier.go +72 -0
  346. package/dist/resources/templates/go-backend/internal/common/types/types.go +411 -0
  347. package/dist/resources/templates/go-backend/internal/common/types/types_test.go +295 -0
  348. package/dist/resources/templates/go-backend/internal/common/utils/constants.go +100 -0
  349. package/dist/resources/templates/go-backend/internal/common/utils/crypto.go +158 -0
  350. package/dist/resources/templates/go-backend/internal/common/utils/crypto_test.go +345 -0
  351. package/dist/resources/templates/go-backend/internal/common/utils/duration.go +25 -0
  352. package/dist/resources/templates/go-backend/internal/common/utils/helpers.go +18 -0
  353. package/dist/resources/templates/go-backend/internal/common/utils/json.go +62 -0
  354. package/dist/resources/templates/go-backend/internal/common/utils/json_test.go +76 -0
  355. package/dist/resources/templates/go-backend/internal/common/utils/pagination.go +51 -0
  356. package/dist/resources/templates/go-backend/internal/common/utils/pagination_test.go +45 -0
  357. package/dist/resources/templates/go-backend/internal/common/utils/pointer.go +107 -0
  358. package/dist/resources/templates/go-backend/internal/common/utils/pointer_test.go +15 -0
  359. package/dist/resources/templates/go-backend/internal/common/utils/slice.go +24 -0
  360. package/dist/resources/templates/go-backend/internal/common/utils/snowflake.go +110 -0
  361. package/dist/resources/templates/go-backend/internal/common/utils/snowflake_test.go +316 -0
  362. package/dist/resources/templates/go-backend/internal/common/utils/string.go +16 -0
  363. package/dist/resources/templates/go-backend/internal/common/utils/timefmt.go +42 -0
  364. package/dist/resources/templates/go-backend/internal/common/validator/validator.go +136 -0
  365. package/dist/resources/templates/go-backend/internal/common/validator/validator_test.go +240 -0
  366. package/dist/resources/templates/go-backend/internal/common/youtubeapi/client.go +224 -0
  367. package/dist/resources/templates/go-backend/internal/core/apicontract/document.go +269 -0
  368. package/dist/resources/templates/go-backend/internal/core/apicontract/document_test.go +104 -0
  369. package/dist/resources/templates/go-backend/internal/core/apicontract/route_validator.go +78 -0
  370. package/dist/resources/templates/go-backend/internal/core/apicontract/route_validator_test.go +108 -0
  371. package/dist/resources/templates/go-backend/internal/core/apidocs/assets/scalar-api-reference.js +2385 -0
  372. package/dist/resources/templates/go-backend/internal/core/apidocs/assets/scalar-api-reference.metadata.json +10 -0
  373. package/dist/resources/templates/go-backend/internal/core/apidocs/route.go +107 -0
  374. package/dist/resources/templates/go-backend/internal/core/apidocs/route_test.go +200 -0
  375. package/dist/resources/templates/go-backend/internal/core/apidocs/scalar.go +117 -0
  376. package/dist/resources/templates/go-backend/internal/core/apidocs/scalar_test.go +99 -0
  377. package/dist/resources/templates/go-backend/internal/core/audit/access_log_policy.go +91 -0
  378. package/dist/resources/templates/go-backend/internal/core/audit/access_log_policy_test.go +51 -0
  379. package/dist/resources/templates/go-backend/internal/core/audit/actor_resolver.go +36 -0
  380. package/dist/resources/templates/go-backend/internal/core/audit/constants.go +30 -0
  381. package/dist/resources/templates/go-backend/internal/core/audit/context.go +119 -0
  382. package/dist/resources/templates/go-backend/internal/core/audit/context_test.go +310 -0
  383. package/dist/resources/templates/go-backend/internal/core/audit/dto.go +468 -0
  384. package/dist/resources/templates/go-backend/internal/core/audit/dto_admin.go +172 -0
  385. package/dist/resources/templates/go-backend/internal/core/audit/enum.go +460 -0
  386. package/dist/resources/templates/go-backend/internal/core/audit/enum_test.go +314 -0
  387. package/dist/resources/templates/go-backend/internal/core/audit/errors.go +9 -0
  388. package/dist/resources/templates/go-backend/internal/core/audit/handler.go +568 -0
  389. package/dist/resources/templates/go-backend/internal/core/audit/listener.go +319 -0
  390. package/dist/resources/templates/go-backend/internal/core/audit/listener_test.go +98 -0
  391. package/dist/resources/templates/go-backend/internal/core/audit/middleware.go +669 -0
  392. package/dist/resources/templates/go-backend/internal/core/audit/middleware_test.go +268 -0
  393. package/dist/resources/templates/go-backend/internal/core/audit/model.go +306 -0
  394. package/dist/resources/templates/go-backend/internal/core/audit/partition_job.go +316 -0
  395. package/dist/resources/templates/go-backend/internal/core/audit/ports.go +9 -0
  396. package/dist/resources/templates/go-backend/internal/core/audit/provider.go +30 -0
  397. package/dist/resources/templates/go-backend/internal/core/audit/repository.go +1007 -0
  398. package/dist/resources/templates/go-backend/internal/core/audit/repository_test.go +934 -0
  399. package/dist/resources/templates/go-backend/internal/core/audit/router.go +85 -0
  400. package/dist/resources/templates/go-backend/internal/core/audit/service.go +770 -0
  401. package/dist/resources/templates/go-backend/internal/core/audit/service_test.go +331 -0
  402. package/dist/resources/templates/go-backend/internal/core/audit/vo_admin.go +475 -0
  403. package/dist/resources/templates/go-backend/internal/core/cache/config.go +30 -0
  404. package/dist/resources/templates/go-backend/internal/core/cache/key.go +41 -0
  405. package/dist/resources/templates/go-backend/internal/core/cache/key_test.go +62 -0
  406. package/dist/resources/templates/go-backend/internal/core/cache/l1.go +153 -0
  407. package/dist/resources/templates/go-backend/internal/core/cache/l1_test.go +212 -0
  408. package/dist/resources/templates/go-backend/internal/core/cache/manager.go +278 -0
  409. package/dist/resources/templates/go-backend/internal/core/cache/manager_provider.go +24 -0
  410. package/dist/resources/templates/go-backend/internal/core/cache/metrics.go +126 -0
  411. package/dist/resources/templates/go-backend/internal/core/cache/namespace.go +46 -0
  412. package/dist/resources/templates/go-backend/internal/core/cache/namespace_test.go +90 -0
  413. package/dist/resources/templates/go-backend/internal/core/cache/options.go +39 -0
  414. package/dist/resources/templates/go-backend/internal/core/cache/options_test.go +96 -0
  415. package/dist/resources/templates/go-backend/internal/core/cache/redis.go +311 -0
  416. package/dist/resources/templates/go-backend/internal/core/cache/redis_test.go +51 -0
  417. package/dist/resources/templates/go-backend/internal/core/cache/serializer.go +31 -0
  418. package/dist/resources/templates/go-backend/internal/core/cache/serializer_test.go +139 -0
  419. package/dist/resources/templates/go-backend/internal/core/cache/singleflight.go +53 -0
  420. package/dist/resources/templates/go-backend/internal/core/cache/singleflight_test.go +211 -0
  421. package/dist/resources/templates/go-backend/internal/core/cache/warming.go +86 -0
  422. package/dist/resources/templates/go-backend/internal/core/clock/clock.go +66 -0
  423. package/dist/resources/templates/go-backend/internal/core/clock/clock_test.go +26 -0
  424. package/dist/resources/templates/go-backend/internal/core/clock/runtime_clock.go +283 -0
  425. package/dist/resources/templates/go-backend/internal/core/clock/runtime_clock_test.go +98 -0
  426. package/dist/resources/templates/go-backend/internal/core/commandreplay/errors.go +6 -0
  427. package/dist/resources/templates/go-backend/internal/core/commandreplay/model.go +90 -0
  428. package/dist/resources/templates/go-backend/internal/core/commandreplay/model_test.go +79 -0
  429. package/dist/resources/templates/go-backend/internal/core/commandreplay/namespace.go +7 -0
  430. package/dist/resources/templates/go-backend/internal/core/commandreplay/persistence.go +269 -0
  431. package/dist/resources/templates/go-backend/internal/core/commandreplay/persistence_test.go +185 -0
  432. package/dist/resources/templates/go-backend/internal/core/commandreplay/provider.go +9 -0
  433. package/dist/resources/templates/go-backend/internal/core/commandreplay/repository.go +22 -0
  434. package/dist/resources/templates/go-backend/internal/core/commandreplay/service.go +853 -0
  435. package/dist/resources/templates/go-backend/internal/core/commandreplay/service_persistence_test.go +432 -0
  436. package/dist/resources/templates/go-backend/internal/core/commandreplay/service_test.go +891 -0
  437. package/dist/resources/templates/go-backend/internal/core/commandreplay/types.go +121 -0
  438. package/dist/resources/templates/go-backend/internal/core/config/config.go +2069 -0
  439. package/dist/resources/templates/go-backend/internal/core/config/config_test.go +1122 -0
  440. package/dist/resources/templates/go-backend/internal/core/config/contract/contract_test.go +388 -0
  441. package/dist/resources/templates/go-backend/internal/core/config/contract/docs.go +198 -0
  442. package/dist/resources/templates/go-backend/internal/core/config/contract/verify.go +424 -0
  443. package/dist/resources/templates/go-backend/internal/core/config/enum.go +46 -0
  444. package/dist/resources/templates/go-backend/internal/core/config/metadata.go +717 -0
  445. package/dist/resources/templates/go-backend/internal/core/config/metadata_test.go +228 -0
  446. package/dist/resources/templates/go-backend/internal/core/config/product_admin_config_test.go +210 -0
  447. package/dist/resources/templates/go-backend/internal/core/crypto/aes_gcm.go +111 -0
  448. package/dist/resources/templates/go-backend/internal/core/crypto/aes_gcm_test.go +364 -0
  449. package/dist/resources/templates/go-backend/internal/core/crypto/encryptor.go +12 -0
  450. package/dist/resources/templates/go-backend/internal/core/crypto/envelope_encryptor.go +134 -0
  451. package/dist/resources/templates/go-backend/internal/core/crypto/envelope_encryptor_test.go +118 -0
  452. package/dist/resources/templates/go-backend/internal/core/crypto/pem.go +112 -0
  453. package/dist/resources/templates/go-backend/internal/core/crypto/pem_test.go +44 -0
  454. package/dist/resources/templates/go-backend/internal/core/crypto/provider.go +130 -0
  455. package/dist/resources/templates/go-backend/internal/core/crypto/provider_test.go +92 -0
  456. package/dist/resources/templates/go-backend/internal/core/database/base_model.go +24 -0
  457. package/dist/resources/templates/go-backend/internal/core/database/clickhouse.go +121 -0
  458. package/dist/resources/templates/go-backend/internal/core/database/database.go +112 -0
  459. package/dist/resources/templates/go-backend/internal/core/database/database_logger_test.go +95 -0
  460. package/dist/resources/templates/go-backend/internal/core/database/errors.go +25 -0
  461. package/dist/resources/templates/go-backend/internal/core/database/metrics_plugin.go +105 -0
  462. package/dist/resources/templates/go-backend/internal/core/database/pool_collector.go +71 -0
  463. package/dist/resources/templates/go-backend/internal/core/database/read_replica.go +520 -0
  464. package/dist/resources/templates/go-backend/internal/core/database/transaction.go +170 -0
  465. package/dist/resources/templates/go-backend/internal/core/database/transaction_test.go +282 -0
  466. package/dist/resources/templates/go-backend/internal/core/distlock/lock.go +353 -0
  467. package/dist/resources/templates/go-backend/internal/core/distlock/lock_test.go +106 -0
  468. package/dist/resources/templates/go-backend/internal/core/errors/handler.go +189 -0
  469. package/dist/resources/templates/go-backend/internal/core/errors/handler_test.go +515 -0
  470. package/dist/resources/templates/go-backend/internal/core/eventbus/eventbus.go +667 -0
  471. package/dist/resources/templates/go-backend/internal/core/eventbus/eventbus_test.go +792 -0
  472. package/dist/resources/templates/go-backend/internal/core/eventstream/noop.go +22 -0
  473. package/dist/resources/templates/go-backend/internal/core/eventstream/provider.go +20 -0
  474. package/dist/resources/templates/go-backend/internal/core/eventstream/publisher.go +38 -0
  475. package/dist/resources/templates/go-backend/internal/core/eventstream/redis.go +109 -0
  476. package/dist/resources/templates/go-backend/internal/core/eventstream/routing.go +104 -0
  477. package/dist/resources/templates/go-backend/internal/core/eventstream/routing_test.go +71 -0
  478. package/dist/resources/templates/go-backend/internal/core/geoip/model.go +19 -0
  479. package/dist/resources/templates/go-backend/internal/core/geoip/service.go +88 -0
  480. package/dist/resources/templates/go-backend/internal/core/health/health.go +333 -0
  481. package/dist/resources/templates/go-backend/internal/core/health/health_test.go +27 -0
  482. package/dist/resources/templates/go-backend/internal/core/httpclient/client.go +772 -0
  483. package/dist/resources/templates/go-backend/internal/core/httpclient/client_test.go +287 -0
  484. package/dist/resources/templates/go-backend/internal/core/httpclient/download.go +578 -0
  485. package/dist/resources/templates/go-backend/internal/core/httpclient/download_test.go +374 -0
  486. package/dist/resources/templates/go-backend/internal/core/jwtkeyring/jwk.go +38 -0
  487. package/dist/resources/templates/go-backend/internal/core/jwtkeyring/keyring.go +144 -0
  488. package/dist/resources/templates/go-backend/internal/core/jwtkeyring/keyring_test.go +128 -0
  489. package/dist/resources/templates/go-backend/internal/core/jwtkeyring/registry.go +60 -0
  490. package/dist/resources/templates/go-backend/internal/core/logger/logger.go +53 -0
  491. package/dist/resources/templates/go-backend/internal/core/logger/logger_test.go +194 -0
  492. package/dist/resources/templates/go-backend/internal/core/observability/metrics.go +358 -0
  493. package/dist/resources/templates/go-backend/internal/core/observability/sentry.go +445 -0
  494. package/dist/resources/templates/go-backend/internal/core/observability/sentry_privacy_test.go +175 -0
  495. package/dist/resources/templates/go-backend/internal/core/observability/tracing.go +187 -0
  496. package/dist/resources/templates/go-backend/internal/core/outbox/model.go +314 -0
  497. package/dist/resources/templates/go-backend/internal/core/outbox/model_test.go +77 -0
  498. package/dist/resources/templates/go-backend/internal/core/outbox/persistence.go +51 -0
  499. package/dist/resources/templates/go-backend/internal/core/outbox/provider.go +23 -0
  500. package/dist/resources/templates/go-backend/internal/core/outbox/relay.go +1138 -0
  501. package/dist/resources/templates/go-backend/internal/core/outbox/relay_postgres_test.go +257 -0
  502. package/dist/resources/templates/go-backend/internal/core/outbox/relay_test.go +690 -0
  503. package/dist/resources/templates/go-backend/internal/core/outbox/rescheduler.go +164 -0
  504. package/dist/resources/templates/go-backend/internal/core/outbox/rescheduler_test.go +209 -0
  505. package/dist/resources/templates/go-backend/internal/core/outbox/writer.go +249 -0
  506. package/dist/resources/templates/go-backend/internal/core/outbox/writer_postgres_test.go +170 -0
  507. package/dist/resources/templates/go-backend/internal/core/outbox/writer_test.go +123 -0
  508. package/dist/resources/templates/go-backend/internal/core/provider.go +198 -0
  509. package/dist/resources/templates/go-backend/internal/core/queue/metrics_middleware.go +38 -0
  510. package/dist/resources/templates/go-backend/internal/core/queue/metrics_middleware_test.go +12 -0
  511. package/dist/resources/templates/go-backend/internal/core/queue/queue.go +288 -0
  512. package/dist/resources/templates/go-backend/internal/core/queue/queue_test.go +159 -0
  513. package/dist/resources/templates/go-backend/internal/core/resilience/bulkhead.go +514 -0
  514. package/dist/resources/templates/go-backend/internal/core/resilience/bulkhead_test.go +984 -0
  515. package/dist/resources/templates/go-backend/internal/core/resilience/circuitbreaker.go +358 -0
  516. package/dist/resources/templates/go-backend/internal/core/resilience/circuitbreaker_test.go +788 -0
  517. package/dist/resources/templates/go-backend/internal/core/resilience/retry.go +318 -0
  518. package/dist/resources/templates/go-backend/internal/core/resilience/retry_test.go +735 -0
  519. package/dist/resources/templates/go-backend/internal/core/schedule/recorder.go +305 -0
  520. package/dist/resources/templates/go-backend/internal/core/schedule/run_context.go +124 -0
  521. package/dist/resources/templates/go-backend/internal/core/schedule/run_context_test.go +85 -0
  522. package/dist/resources/templates/go-backend/internal/core/schedule/scheduler.go +1589 -0
  523. package/dist/resources/templates/go-backend/internal/core/schedule/scheduler_control_test.go +808 -0
  524. package/dist/resources/templates/go-backend/internal/core/storage/oss_provider.go +398 -0
  525. package/dist/resources/templates/go-backend/internal/core/storage/oss_provider_test.go +89 -0
  526. package/dist/resources/templates/go-backend/internal/core/storage/path.go +110 -0
  527. package/dist/resources/templates/go-backend/internal/core/storage/path_test.go +301 -0
  528. package/dist/resources/templates/go-backend/internal/core/storage/s3_provider.go +489 -0
  529. package/dist/resources/templates/go-backend/internal/core/storage/s3_provider_test.go +60 -0
  530. package/dist/resources/templates/go-backend/internal/core/storage/storage.go +268 -0
  531. package/dist/resources/templates/go-backend/internal/core/storage/validation.go +225 -0
  532. package/dist/resources/templates/go-backend/internal/core/storage/validation_test.go +495 -0
  533. package/dist/resources/templates/go-backend/internal/core/temporal/logger.go +38 -0
  534. package/dist/resources/templates/go-backend/internal/core/temporal/provider.go +126 -0
  535. package/dist/resources/templates/go-backend/internal/core/temporal/provider_test.go +67 -0
  536. package/dist/resources/templates/go-backend/internal/core/websocket/client.go +282 -0
  537. package/dist/resources/templates/go-backend/internal/core/websocket/client_test.go +81 -0
  538. package/dist/resources/templates/go-backend/internal/core/websocket/config.go +58 -0
  539. package/dist/resources/templates/go-backend/internal/core/websocket/handler.go +53 -0
  540. package/dist/resources/templates/go-backend/internal/core/websocket/hub.go +269 -0
  541. package/dist/resources/templates/go-backend/internal/core/websocket/message.go +273 -0
  542. package/dist/resources/templates/go-backend/internal/core/websocket/notifier.go +92 -0
  543. package/dist/resources/templates/go-backend/internal/core/websocket/provider.go +33 -0
  544. package/dist/resources/templates/go-backend/internal/integrations/onex/iam/action.go +77 -0
  545. package/dist/resources/templates/go-backend/internal/integrations/onex/iam/action_test.go +238 -0
  546. package/dist/resources/templates/go-backend/internal/integrations/onex/iam/condition.go +107 -0
  547. package/dist/resources/templates/go-backend/internal/integrations/onex/iam/decision.go +256 -0
  548. package/dist/resources/templates/go-backend/internal/integrations/onex/iam/enum.go +643 -0
  549. package/dist/resources/templates/go-backend/internal/integrations/onex/iam/errors.go +102 -0
  550. package/dist/resources/templates/go-backend/internal/integrations/onex/iam/principal_record.go +21 -0
  551. package/dist/resources/templates/go-backend/internal/integrations/onex/iam/product_context.go +486 -0
  552. package/dist/resources/templates/go-backend/internal/integrations/onex/iam/resource.go +215 -0
  553. package/dist/resources/templates/go-backend/internal/integrations/onex/iam/resource_test.go +242 -0
  554. package/dist/resources/templates/go-backend/internal/products/README.md +5 -0
  555. package/dist/resources/templates/go-backend/internal/sharedevents/README.md +5 -0
  556. package/dist/resources/templates/go-backend/migrations/000001_infrastructure.down.sql +13 -0
  557. package/dist/resources/templates/go-backend/migrations/000001_infrastructure.up.sql +886 -0
  558. package/dist/resources/templates/go-backend/pkg/apierror/error.go +327 -0
  559. package/dist/resources/templates/go-backend/pkg/apierror/error_code.go +65 -0
  560. package/dist/resources/templates/go-backend/pkg/apierror/error_test.go +41 -0
  561. package/dist/resources/templates/go-backend/pkg/apierror/error_type.go +14 -0
  562. package/dist/resources/templates/go-backend/pkg/apierror/http_response.go +216 -0
  563. package/dist/resources/templates/go-backend/pkg/apierror/http_response_test.go +216 -0
  564. package/dist/resources/templates/go-backend/scripts/check-openapi-clean-deletion.sh +54 -0
  565. package/dist/resources/templates/go-backend/scripts/test-migrations.sh +32 -0
  566. package/dist/resources/templates/go-backend/tests/infrastructure.sql +69 -0
  567. package/dist/resources/templates/mcp-server/.claude/docs/stack.md +338 -0
  568. package/dist/resources/templates/mcp-server/.editorconfig +13 -0
  569. package/dist/resources/templates/mcp-server/.env.example +8 -0
  570. package/dist/resources/templates/mcp-server/.github/ISSUE_TEMPLATE/bug_report.yml +104 -0
  571. package/dist/resources/templates/mcp-server/.github/ISSUE_TEMPLATE/feature_request.yml +54 -0
  572. package/dist/resources/templates/mcp-server/.github/PULL_REQUEST_TEMPLATE.md +36 -0
  573. package/dist/resources/templates/mcp-server/.github/workflows/ci.yml +78 -0
  574. package/dist/resources/templates/mcp-server/.husky/commit-msg +1 -0
  575. package/dist/resources/templates/mcp-server/.husky/pre-commit +1 -0
  576. package/dist/resources/templates/mcp-server/.lintstagedrc.json +4 -0
  577. package/dist/resources/templates/mcp-server/.nvmrc +1 -0
  578. package/dist/resources/templates/mcp-server/.prettierrc +8 -0
  579. package/dist/resources/templates/mcp-server/CONTRIBUTING.md +184 -0
  580. package/dist/resources/templates/mcp-server/Dockerfile +23 -0
  581. package/dist/resources/templates/mcp-server/LICENSE +21 -0
  582. package/dist/resources/templates/mcp-server/README.md +123 -0
  583. package/dist/resources/templates/mcp-server/_gitignore +26 -0
  584. package/dist/resources/templates/mcp-server/commitlint.config.js +24 -0
  585. package/dist/resources/templates/mcp-server/eslint.config.js +30 -0
  586. package/dist/resources/templates/mcp-server/package.json +72 -0
  587. package/dist/resources/templates/mcp-server/src/index.ts +27 -0
  588. package/dist/resources/templates/mcp-server/src/resources.ts +33 -0
  589. package/dist/resources/templates/mcp-server/src/services/.gitkeep +0 -0
  590. package/dist/resources/templates/mcp-server/src/tools.ts +43 -0
  591. package/dist/resources/templates/mcp-server/src/types/index.ts +19 -0
  592. package/dist/resources/templates/mcp-server/src/utils/index.ts +31 -0
  593. package/dist/resources/templates/mcp-server/tsconfig.json +22 -0
  594. package/dist/resources/templates/mcp-server/tsup.config.ts +14 -0
  595. package/dist/resources/templates/mcp-server/vitest.config.ts +15 -0
  596. package/dist/resources/templates/nuxt/.claude/docs/stack.md +666 -0
  597. package/dist/resources/templates/nuxt/.editorconfig +15 -0
  598. package/dist/resources/templates/nuxt/.env.example +22 -0
  599. package/dist/resources/templates/nuxt/.github/workflows/ci.yml +34 -0
  600. package/dist/resources/templates/nuxt/.husky/commit-msg +1 -0
  601. package/dist/resources/templates/nuxt/.husky/pre-commit +1 -0
  602. package/dist/resources/templates/nuxt/.nvmrc +1 -0
  603. package/dist/resources/templates/nuxt/.prettierignore +9 -0
  604. package/dist/resources/templates/nuxt/.prettierrc +1 -0
  605. package/dist/resources/templates/nuxt/README.md +23 -0
  606. package/dist/resources/templates/nuxt/_gitignore +7 -0
  607. package/dist/resources/templates/nuxt/app/app.config.ts +105 -0
  608. package/dist/resources/templates/nuxt/app/i18n/locales/en.json +5 -0
  609. package/dist/resources/templates/nuxt/app/pages/app/index.vue +224 -0
  610. package/dist/resources/templates/nuxt/app/pages/authors/[slug].vue +37 -0
  611. package/dist/resources/templates/nuxt/app/pages/blog/[slug].vue +55 -0
  612. package/dist/resources/templates/nuxt/app/pages/blog/category/[category].vue +38 -0
  613. package/dist/resources/templates/nuxt/app/pages/blog/index.vue +46 -0
  614. package/dist/resources/templates/nuxt/app/pages/index.vue +626 -0
  615. package/dist/resources/templates/nuxt/app/pages/legal/privacy.vue +21 -0
  616. package/dist/resources/templates/nuxt/app/pages/legal/terms.vue +21 -0
  617. package/dist/resources/templates/nuxt/app/pages/ui/index.vue +397 -0
  618. package/dist/resources/templates/nuxt/commitlint.config.mjs +1 -0
  619. package/dist/resources/templates/nuxt/eslint.config.mjs +4 -0
  620. package/dist/resources/templates/nuxt/lint-staged.config.mjs +8 -0
  621. package/dist/resources/templates/nuxt/nuxt.config.ts +71 -0
  622. package/dist/resources/templates/nuxt/package.json +67 -0
  623. package/dist/resources/templates/nuxt/public/apple-touch-icon.png +0 -0
  624. package/dist/resources/templates/nuxt/public/favicon.ico +0 -0
  625. package/dist/resources/templates/nuxt/public/favicon.svg +5 -0
  626. package/dist/resources/templates/nuxt/public/icon-16x16.png +0 -0
  627. package/dist/resources/templates/nuxt/public/icon-192x192.png +0 -0
  628. package/dist/resources/templates/nuxt/public/icon-32x32.png +0 -0
  629. package/dist/resources/templates/nuxt/public/icon-512x512.png +0 -0
  630. package/dist/resources/templates/nuxt/public/logo.svg +6 -0
  631. package/dist/resources/templates/nuxt/public/media/hero-product-gallery.png +0 -0
  632. package/dist/resources/templates/nuxt/server/api/content/sitemap-urls.get.ts +12 -0
  633. package/dist/resources/templates/nuxt/server/api/preview.get.ts +9 -0
  634. package/dist/resources/templates/nuxt/stylelint.config.mjs +3 -0
  635. package/dist/resources/templates/nuxt/test/e2e/rendering.test.ts +13 -0
  636. package/dist/resources/templates/nuxt/tsconfig.json +3 -0
  637. package/dist/resources/templates/nuxt/vitest.config.ts +8 -0
  638. package/package.json +8 -5
@@ -0,0 +1,917 @@
1
+ # Mediagen Provider Onboarding SOP
2
+
3
+ > **Upstream scope**: this complete OneX reference retains its explicitly dated provider snapshot. It describes work inside the upstream Mediagen service, not product-local provider implementation. Current upstream contracts and provider documentation must be verified before use; snapshot counts and paths are not current deployment evidence.
4
+
5
+ > Scope: `internal/platform/mediagen/infrastructure/providers/*` + admin runtime configuration plane
6
+ > Audience: 任何需要新增 / 修改 / 排障 mediagen Provider Adapter 或在 admin 上线 Provider 的工程师
7
+ > 状态:admin-integrated 锁定版(基于提交 `f2338a6d` → `211ed024`)
8
+ > 设计权威:本文与 `backend/.claude/docs/identifier-contract.md`、`api-design.md`、`error-handling.md` 共同构成 mediagen 模块的技术契约。
9
+
10
+ 本文档目的:让"接入新 Provider"从一个**模糊的探索性任务**收敛为**一组可逐步勾选的工程操作**。每一步都有具体的文件路径、迁移编号、admin UI 入口、复制模板、验收门槛。**严格按照本 SOP 落地的新 Provider,零返工**。
11
+
12
+ post-alignment 阶段(commits `f2338a6d` → `84953e39`)锁定了 adapter 失败归一化、lane snapshot 真相源、telemetry 强制等架构不变量;admin-integrated 阶段(commits `fc1ee8d6` → `211ed024`)把"运营人员只能写 SQL 改 lane 配置"的现状升级为"在 admin UI 全流程可视化操作 + 服务端 JSON Schema 强校验 + Dry-run + Diff + Rollback"。本 SOP 同时覆盖两条路径:迁移文件(reproducible 种子)和 admin UI(运营自服务)。
13
+
14
+ ---
15
+
16
+ ## Section 1: Provider × Capability × Endpoint Matrix
17
+
18
+ ### 1.1 已激活 Provider 概要(snapshot 2026-04-26)
19
+
20
+ | # | Provider Code | Base URL | Auth Scheme | Transport Class | Operations | Submit Endpoint Source | Poll Endpoint Source | Cancel | 说明 |
21
+ |---|---------------|----------|-------------|-----------------|-----------|------------------------|-----------------------|--------|-----|
22
+ | 1 | `duomi` | `https://duomiapi.com` | submit: query `?key=`(secondary);poll: header `Authorization: <raw token>`(primary,无 Bearer 前缀) | `http_json_poll` | t2i, i2i, t2v, i2v | `req.Endpoint`(必填) | `firstNonEmpty(req.Endpoint, "/api/video/pix/feed?task_id=%s")` | 不支持 | 双凭证 split-channel;nano-banana 同步图像生成走 `sync:<base64>` ExternalID 短路(见 §1.4) |
23
+ | 2 | `wavespeed` | `https://api.wavespeed.ai` | header `Authorization: Bearer <api_key>` | `http_json_poll` | t2i, i2i, t2v, i2v | `req.Endpoint`(必填) | `pollEndpoint = "/api/v3/predictions/%s/result"`(adapter const) | 不支持 | 内容审核走 `has_nsfw_contents[]` 后置探测,adapter 合成 `nsfw` provider_error_code |
24
+ | 3 | `vidu` | `https://api.vidu.com` | header `Authorization: Token <api_key>`("Token " 前缀必填) | `http_json_poll` | i2v | `req.Endpoint`(必填) | `pollEndpoint = "/ent/v2/tasks/%s/creations"`(adapter const) | 不支持(off_peak 任务可支持,未启用) | 错误码字符串枚举(`BadRequest` / `TooManyRequests` / `AuditFailed` 等),从 `{code,reason,message}` 信封解析 |
25
+ | 4 | `wan` | `https://dashscope.aliyuncs.com` | header `Authorization: Bearer <api_key>` + 必填 `X-DashScope-Async: enable` | `http_json_poll` | t2v, i2v | `req.Endpoint`(必填) | `pollEndpoint = "/api/v1/tasks/%s"`(adapter const,Dashscope 通用) | 不支持 | 状态枚举大写 `PENDING/RUNNING/SUCCEEDED/FAILED/SUSPENDED/CANCELED/UNKNOWN`;失败 code 在 `output.code` |
26
+ | 5 | `pixfree` | `https://app-api.pixverse.ai/creative_platform` | n/a(browser automation 子系统注入) | `browser_automation` | t2v, i2v, extend, lip_sync | n/a(Temporal stealthActivity 内部组装) | n/a | 不支持 | 唯一非 HTTP transport;Submit/Poll 均委派 Temporal `stealthActivity`,按 model_code 路由到 5 种 payload |
27
+ | 6 | `yunwu` | `https://yunwu.ai` | header `Authorization: Bearer <api_key>` | `http_json_poll` | t2v, i2v | `req.Endpoint`(必填) | `pollEndpoint = "/v1/video/query?id=%s"`(adapter const) | 不支持 | HTTP 200 + `error` 字段非空表示业务级失败,adapter 强制 `Status=Failed` |
28
+
29
+ ### 1.2 Capability 矩阵
30
+
31
+ | Operation | duomi | wavespeed | vidu | wan | pixfree | yunwu |
32
+ |-----------|:-----:|:---------:|:----:|:---:|:-------:|:-----:|
33
+ | `t2i` | ✓ | ✓ | - | - | - | - |
34
+ | `i2i` | ✓ | ✓ | - | - | - | - |
35
+ | `t2v` | ✓ | ✓ | - | ✓ | ✓ | ✓ |
36
+ | `i2v` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
37
+ | `v2v` | - | - | - | - | - | - |
38
+ | `extend` | - | - | - | - | ✓ | - |
39
+ | `lip_sync` | - | - | - | - | ✓ | - |
40
+ | `upscale` | - | - | - | - | - | - |
41
+ | `frame_extract` | - | - | - | - | - | - |
42
+
43
+ > 共计 **27 条 active lanes**。`v2v` / `upscale` / `frame_extract` 三个 operation 在 CHECK 约束中保留但当前无 lane。
44
+
45
+ ### 1.3 各 Provider 现存 Lanes 明细
46
+
47
+ | Provider | Lane Code | Operation | Modality | Submit Path |
48
+ |----------|-----------|-----------|----------|-------------|
49
+ | duomi | `duomi_flux_schnell__t2i__duomi` | t2i | image | `/v1/images/generations`(同步) |
50
+ | duomi | `duomi_nano_banana__t2i__duomi` | t2i | image | `/api/gemini/nano-banana`(同步) |
51
+ | duomi | `duomi_nano_banana_pro__t2i__duomi` | t2i | image | `/api/gemini/nano-banana`(同步) |
52
+ | duomi | `duomi_nano_banana_edit__i2i__duomi` | i2i | image | `/api/gemini/nano-banana-edit`(同步) |
53
+ | duomi | `duomi_nano_banana_pro_edit__i2i__duomi` | i2i | image | `/api/gemini/nano-banana-edit`(同步) |
54
+ | duomi | `duomi_pixpro_kling__t2v__duomi` | t2v | video | `/api/video/pix/pro/generate` |
55
+ | duomi | `duomi_pixpro_kling__i2v__duomi` | i2v | video | `/api/video/pix/pro/generate` |
56
+ | wavespeed | `wavespeed_nano_banana_t2i__t2i__wavespeed` | t2i | image | `/api/v3/google/nano-banana/text-to-image` |
57
+ | wavespeed | `wavespeed_nano_banana_pro_t2i__t2i__wavespeed` | t2i | image | `/api/v3/google/nano-banana-pro/text-to-image` |
58
+ | wavespeed | `wavespeed_nano_banana_edit__i2i__wavespeed` | i2i | image | `/api/v3/google/nano-banana/edit` |
59
+ | wavespeed | `wavespeed_nano_banana_pro_edit__i2i__wavespeed` | i2i | image | `/api/v3/google/nano-banana-pro/edit` |
60
+ | wavespeed | `wavespeed_seedream_v4_edit__i2i__wavespeed` | i2i | image | `/api/v3/bytedance/seedream-v4/edit` |
61
+ | wavespeed | `wavespeed_z_image_turbo_i2i__i2i__wavespeed` | i2i | image | `/api/v3/wavespeed-ai/z-image-turbo/image-to-image` |
62
+ | wavespeed | `wavespeed_seedance_v1pro_t2v__t2v__wavespeed` | t2v | video | `/api/v3/bytedance/seedance-v1-pro-fast/text-to-video` |
63
+ | wavespeed | `wavespeed_wan22_t2v_ultrafast__t2v__wavespeed` | t2v | video | `/api/v3/wavespeed-ai/wan-2.2/t2v-480p-ultra-fast` |
64
+ | wavespeed | `wavespeed_seedance_v1pro_i2v__i2v__wavespeed` | i2v | video | `/api/v3/bytedance/seedance-v1-pro-fast/image-to-video` |
65
+ | wavespeed | `wavespeed_wan22_i2v_ultrafast__i2v__wavespeed` | i2v | video | `/api/v3/wavespeed-ai/wan-2.2/i2v-480p-ultra-fast` |
66
+ | vidu | `vidu_template2video__i2v__vidu` | i2v | video | `/ent/v2/template2video` |
67
+ | wan | `wan_t2v__t2v__wan` | t2v | video | `/api/v1/services/aigc/video-generation/video-synthesis` |
68
+ | wan | `wan_i2v__i2v__wan` | i2v | video | `/api/v1/services/aigc/video-generation/video-synthesis` |
69
+ | pixfree | `pixfree_t2v__t2v__pixfree` | t2v | video | `api/createTextVideo`(browser automation) |
70
+ | pixfree | `pixfree_i2v__i2v__pixfree` | i2v | video | `api/createVideo`(browser automation) |
71
+ | pixfree | `pixfree_face_swap__i2v__pixfree` | i2v | video | `api/createTwoImageToVideo`(browser automation) |
72
+ | pixfree | `pixfree_video_extend__extend__pixfree` | extend | video | `api/extendVideo`(browser automation) |
73
+ | pixfree | `pixfree_lip_sync__lip_sync__pixfree` | lip_sync | video | `api/speechVideo`(browser automation) |
74
+ | yunwu | `yunwu_sora2__t2v__yunwu` | t2v | video | `/v1/video/create` |
75
+ | yunwu | `yunwu_sora2__i2v__yunwu` | i2v | video | `/v1/video/create` |
76
+
77
+ ### 1.4 Provider Quirks(坑位备忘)
78
+
79
+ - **duomi 双凭证**:submit/poll 用的是不同的密钥;secondary(PHP 源 `public $key`)走 query,primary(PHP `Authorization` header)走 header。`p_ai_providers.metadata.auth_profiles.{submit,poll}` 必须分别配置。
80
+ - **duomi nano-banana 同步路径**:图像生成端点 `/api/gemini/nano-banana(-edit)` 是同步返回的,adapter 在 Submit 时把图像 base64/URL 直接编码到 `ExternalID` 内(前缀 `sync:`),Poll 阶段识别 `sync:` 前缀走短路逻辑(不再 HTTP 调用),直接返回 `Succeeded` + Output。
81
+ - **wavespeed NSFW**:服务端不会把 NSFW 标记成 status=failed,而是在 `data.has_nsfw_contents: bool[]` 上每个 output 一个 flag;adapter 在 Poll 终态成功后扫描这个数组,命中即合成 `provider_error_code = "nsfw"` 走 `MapProviderFailure` 翻译为 `output_policy_violation`。
82
+ - **vidu auth 前缀**:必须是 `"Token " + api_key`(注意 Token 后面有空格),不是 Bearer,不是裸 token。
83
+ - **wan 异步 header**:`X-DashScope-Async: enable` 缺失会被服务器拒绝,adapter 必填。Dashscope 失败时 HTTP 仍是 200,错误码在 `output.code`(如 `DataInspectionFailed` / `Throttling.RateQuota`)。
84
+ - **pixfree non-HTTP**:`browser_automation` transport 没有 HTTP submit/poll 端点;adapter 通过 Temporal `stealthActivity` 把任务交给浏览器自动化 worker 执行。`req.Endpoint` 在此 transport class 下不读。
85
+ - **yunwu HTTP 200 + error**:业务级失败 HTTP 仍是 200,`error` 字段非空,adapter 必须强制 `Status=Failed`,否则 poll loop 会无限轮询吞噬终态。
86
+
87
+ ---
88
+
89
+ ## Section 2: 标准化设计原则(Architectural Invariants)
90
+
91
+ post-alignment 后,下列不变量是 mediagen Provider Adapter 的**法律**。新增 Provider、修改现有 Provider、排障,均不得破坏。
92
+
93
+ ### 2.1 单一失败归一化点:`base.MapProviderFailure`
94
+
95
+ 所有 adapter 的失败路径**只能**通过 `base.MapProviderFailure(ctx FailureContext)` 翻译成 `*ProviderError`。
96
+
97
+ ```go
98
+ // 对应 internal/platform/mediagen/infrastructure/providers/base/failure_mapping.go
99
+ return base.MapProviderFailure(base.FailureContext{
100
+ ProviderCode: providerCode,
101
+ Phase: domain.FailurePhaseSubmit, // or Poll / Cancel
102
+ HTTPStatus: resp.StatusCode,
103
+ ProviderErrorCode: "<provider 文档定义的稳定字符串 code>",
104
+ ProviderStatus: "<业务级状态,仅在 HTTP 200 + 业务失败时填>",
105
+ Message: "<人类可读错误,可空>",
106
+ LaneMapper: req.FailureMapper, // 必传,活动层从 laneSnapshot 注入
107
+ })
108
+ ```
109
+
110
+ `MapProviderFailure` 内部按四维优先级(ProviderErrorCode > ProviderStatus > HTTPStatus > Message)走 lane snapshot 的 `failure_mapper` JSON 规则 + 默认规则,得到稳定 `domain.FailureReasonCode`。Adapter **绝不**自行实现 `mapXxxFailure`。
111
+
112
+ ### 2.2 文案匹配禁令:禁止 `strings.Contains` 做错误分类
113
+
114
+ 整个 mediagen 模块**全面禁止**以下模式:
115
+
116
+ ```go
117
+ // FORBIDDEN
118
+ if strings.Contains(err.Error(), "rate limit") { ... }
119
+ if strings.Contains(message, "content policy") { ... }
120
+ ```
121
+
122
+ 错误分类必须基于**结构化信号**:sentinel error(`errors.Is`)、Temporal `ApplicationError` 类型、JSON envelope 的稳定 code 字段。文案变化不得影响系统行为。
123
+
124
+ ### 2.3 Lane snapshot 是端点真相源
125
+
126
+ - `req.Endpoint`(来自 lane revision 的 `endpoint_config.submit_path`)是 **Submit 路径的唯一真相源**。adapter 必须在 Submit 入口校验 `req.Endpoint != ""`,空值即报错。
127
+ - Poll 端点允许在 adapter 中以 `const` 形式存在,**前提**:该 Provider 的所有 lane 共享同一 poll 路由(如 wavespeed `/api/v3/predictions/%s/result`、wan `/api/v1/tasks/%s` 是 Dashscope 平台级路由)。
128
+ - 当 Provider 不同 lane 需要不同 poll 路由时,参考 duomi 模式:`firstNonEmpty(req.Endpoint, defaultPollTmpl)`,让 lane revision 的 `poll_config.path_template` 可覆盖。
129
+ - **禁止**在 adapter 中 hardcode 任何 submit 路径常量。
130
+
131
+ ### 2.4 `req.FailureMapper` 透传链
132
+
133
+ `domain.SubmitRequest` / `domain.PollRequest` / `domain.CancelRequest` 都带 `FailureMapper map[string]any` 字段。
134
+
135
+ 完整数据流:
136
+ ```
137
+ p_ai_provider_lane_revisions.failure_mapper (JSONB)
138
+ ↓ 装载
139
+ ProviderLaneRevisionSnapshot.FailureMapper
140
+ ↓ Activity 层注入
141
+ domain.SubmitRequest.FailureMapper / PollRequest.FailureMapper / CancelRequest.FailureMapper
142
+ ↓ adapter 透传
143
+ base.MapProviderFailure 入参 LaneMapper
144
+ ↓ 走规则匹配
145
+ domain.FailureReasonCode(稳定枚举)
146
+ ```
147
+
148
+ Adapter **不得**修改、丢弃 `req.FailureMapper`,必须透传到 `MapProviderFailure`。
149
+
150
+ ### 2.5 稳定 `domain.FailureReasonCode` 枚举(9 类)
151
+
152
+ 整个 mediagen 域只有以下 9 个 reason code 是合法的:
153
+
154
+ | Code | 含义 | Retryable |
155
+ |------|------|-----------|
156
+ | `prompt_policy_violation` | Prompt 违反策略(用户文案侧) | 否 |
157
+ | `input_image_policy_violation` | 输入图违反策略 | 否 |
158
+ | `input_image_invalid` | 输入图非法(格式/尺寸/编码) | 否 |
159
+ | `output_policy_violation` | 输出违反策略(含 NSFW) | 否 |
160
+ | `timeout` | 调用超时 | 是 |
161
+ | `rate_limit` | 限流 | 是 |
162
+ | `provider_error` | Provider 5xx / 上游不可用 | HTTP 5xx 时是,其他否 |
163
+ | `internal_error` | 平台内部错误 | 否(需人工) |
164
+ | `user_canceled` | 用户取消 | 否 |
165
+
166
+ > 新增类别需要修改 `domain.FailureReasonCode`、`ResolveFailureReasonCode`、所有 lane snapshot 的 mapper rule。**新接 Provider 时不应触发这条路径**——遇到无法归类的错误,先映射到 `provider_error`。Auth 失败、计费失败、参数错误均归 `provider_error`(HTTP 4xx 不可重试分支)。
167
+
168
+ ### 2.6 Idempotency 文档化
169
+
170
+ - 真正实现幂等的 Provider(目前没有):在 adapter GoDoc 中说明机制(key 名、TTL、生效条件)。
171
+ - Provider 不实现幂等的(多数):**禁止**伪造 `X-Idempotency-Key` header 假装支持幂等。如果代码确实发送了 `X-Idempotency-Key`,必须有注释说明是"内部 trace 标记,Provider 不强制"。
172
+ - 重复提交保护应在 mediagen 域层(`Generation.IdempotencyKey` + DB unique 约束)解决,不依赖 Provider。
173
+
174
+ ### 2.7 Telemetry 强制填充
175
+
176
+ `domain.ProviderTelemetry` 只有 4 个字段:
177
+
178
+ | 字段 | 类型 | 含义 |
179
+ |------|------|------|
180
+ | `HTTPStatus` | `int16` | HTTP 状态码 [100,599];0 表示非 HTTP transport(pixfree) |
181
+ | `LatencyMs` | `int64` | 调用墙钟耗时(毫秒) |
182
+ | `QueueMs` | `int64` | Provider 端排队耗时;不可观测时填 0 |
183
+ | `CostUSD` | `string` | NUMERIC(12,6) 字符串;不可观测时填 `""` |
184
+
185
+ **每个 adapter 在成功路径必须填充 Telemetry**。失败路径不填(`*ProviderError` 自带 StatusCode)。Activity 层 `appendSubmitLog` / `appendPollLog` 把 Telemetry 写入 `p_mediagen_execution_logs`。
186
+
187
+ ### 2.8 `transport_retry_count` = Temporal Activity Attempt
188
+
189
+ - `p_mediagen_generations.transport_retry_count` 列由迁移 000473 创建。
190
+ - 语义:**Temporal SDK 已执行的 Activity attempt 计数减 1**(首次成功不写入;attempt > 1 才落库)。
191
+ - 来源:`activity.GetInfo(ctx).Attempt`(Temporal SDK 提供)。
192
+ - **不是**业务层的"用户重试次数"。用户主动 retry 是新的 Generation。
193
+
194
+ 详见 `domain/generation.go` 的 `TransportRetryCount` GoDoc 与 `provider_activities.go::ProviderPoll` 的写入逻辑。
195
+
196
+ ### 2.9 Lane Revision JSONB 字段服务端强校验(I-9)
197
+
198
+ `aiconfig.ValidateLaneRevisionFields` 在每次 `CreateProviderLaneRevision` 入口处对**所有 12 个 JSONB 字段**做形态契约校验,任何不合法的 JSON **在落库前**被拒绝。这是 D7(production code 无 stub / placeholder)+ E3(zero-tolerance hack)的工程化兜底。
199
+
200
+ 被校验的 12 个字段(与数据库列名 / DTO json tag 一一对应):
201
+
202
+ | # | 字段 | 校验要点 |
203
+ |---|------|---------|
204
+ | 1 | `endpoint_config` | `submit_path` 必填且为合法 path |
205
+ | 2 | `auth_config` | `bindings[].location ∈ {header,query,body,cookie}`;`secret_ref ∈ {primary,secondary}` |
206
+ | 3 | `submit_config` | HTTP method ∈ {POST, GET, PUT}(提交语义);body schema 合法 |
207
+ | 4 | `poll_config` | HTTP method ∈ {POST, GET, PUT, DELETE, PATCH} |
208
+ | 5 | `cancel_config` | 同 transport 通用方法白名单;可空(不支持时) |
209
+ | 6 | `request_defaults` | object,被 request mapper 浅合并 |
210
+ | 7 | `input_schema` | JSON Schema draft-07 子集 |
211
+ | 8 | `request_mapper` | `transforms[]` 每条 `{from, to, op}` 结构合法 |
212
+ | 9 | `response_normalizer` | `paths.{status,output,error}` JSONPath 合法 |
213
+ | 10 | `failure_mapper` | `rules[]` 每条仅含 4 维匹配键(http_status / provider_error_code / provider_status / message_contains)+ 1 个 `reason_code` ∈ §2.5 9 类 |
214
+ | 11 | `telemetry_extractor` | 4 字段 JSONPath 合法(http_status / latency_ms / queue_ms / cost_usd) |
215
+ | 12 | `retry_policy` | `max_attempts ≥ 0`;`backoff` 系数合法;`retryable_codes[]` ∈ §2.5 9 类 |
216
+
217
+ 校验失败时返回 `apierror.BadRequest("lane_revision_invalid", path)`,`path` 形如 `failure_mapper.rules[2].reason_code`,与 Stripe `parameter_invalid` 风格对齐 —— 前端 `FailureMapperRuleBuilder` 等可视化编辑器据此精确定位错误位置。
218
+
219
+ **不变量**:bad config 不再"静默落库 + 运行时 panic",所有形态错误在 admin UI Save 阶段就拒收。
220
+
221
+ ---
222
+
223
+ ## Section 3: 新增 Provider 完整流程 SOP
224
+
225
+ > 这是**核心标准化文档**。每一步都对应具体文件路径或迁移编号。每完成一阶段勾选一次,杜绝跳步。
226
+
227
+ ### Phase A: Research(写代码前)
228
+
229
+ 不做研究就写代码 = 注定返工。每个新 Provider 必须先回答下列 8 个问题,研究结果落到一份 `research-{provider}.md`(可放 `/tmp/`):
230
+
231
+ | # | 必答项 | 示例(duomi) |
232
+ |---|--------|----------------|
233
+ | A1 | base_url(含 scheme + 是否需要尾部斜杠) | `https://duomiapi.com` |
234
+ | A2 | auth scheme(header / query / 其他),prefix(`Bearer ` / `Token ` / 裸 token / `?key=`) | submit query `?key=`;poll header `Authorization: <raw>` |
235
+ | A3 | 提交端点 path(区分 t2i/i2i/t2v/i2v/extend/lip_sync 各操作) | `/api/video/pix/pro/generate`(i2v/t2v);`/api/gemini/nano-banana(-edit)`(t2i/i2i) |
236
+ | A4 | 轮询端点 path(是否所有 lane 共享 / 是否需要 lane-specific) | `/api/video/pix/feed?task_id=%s`(视频族共享) |
237
+ | A5 | 取消端点(不支持就明确写"none") | none |
238
+ | A6 | 状态枚举(大写 / 小写,完整集合) | `pending / running / succeeded / error`(小写) |
239
+ | A7 | 同步 vs 异步(同步要明确返回内容如何编码到 ExternalID) | nano-banana 同步;pix 异步 |
240
+ | A8 | 错误码体系(字符串 / 数字 / 文档化的稳定 enum / 文案描述) | 数字 code 在 envelope 顶层;字符串 reason 在 4xx 信封 |
241
+ | A9 | 内容审核信号(独立 error code / success 中的 flag) | 暂无 |
242
+ | A10 | 成本/计费字段(per-request / 账户级) | 账户级,response 不带 cost |
243
+
244
+ **没有 A1-A10 的全部答案,禁止进入 Phase B**。
245
+
246
+ ---
247
+
248
+ ### Phase B: Database — 注册 Provider + Models + Lane Revisions
249
+
250
+ > 两条互斥路径,按 Provider 是否需要 git-tracked 种子选择:
251
+ >
252
+ > | Path | 何时使用 | 入口 |
253
+ > |------|---------|------|
254
+ > | **B-UI(推荐,运营自服务)** | 仅当前 dev / staging / prod 实例需要这个 Provider;不需要在新 DB freshness 时自动种子 | 走 §K 全流程 admin UI 操作(`/ai/runtime/providers` + `/ai/runtime/lanes` + `/ai/runtime/lanes/:code/revisions`) |
255
+ > | **B-Migration(reproducible 种子)** | Provider 是平台基础能力,每次 fresh DB 都要带(如默认 wavespeed / wan / pixfree);或者需要在 PR 中 review 配置内容 | 写迁移文件,下面 §B.1 / §B.2 / §B.3 模板 |
256
+ >
257
+ > 两条路径的服务端契约**完全相同**:admin UI Save 走 `aiconfig.ValidateLaneRevisionFields`(§2.9),迁移 SQL 落库时不会经过 validator —— **因此走 B-Migration 路径的同学必须严格按 §B.3 模板写,validator 不是 SQL 的兜底**。
258
+
259
+ #### B.1 创建 `00XXXX_seed_<provider>_provider_and_models.up.sql`
260
+
261
+ ```sql
262
+ -- 1. 注册 Provider 行
263
+ INSERT INTO p_ai_providers (
264
+ name, display_name, status,
265
+ base_url, encrypted_api_key, encrypted_secret_key,
266
+ provider_type, max_concurrency, default_timeout_sec, max_retries, default_priority,
267
+ metadata, contact_status, capabilities
268
+ ) VALUES (
269
+ '<provider_code>', '<Display Name>', 'active',
270
+ '<base_url>', '', '',
271
+ 'media', 10, 60, 3, 0,
272
+ jsonb_build_object(
273
+ 'auth_profiles', jsonb_build_object(
274
+ 'submit', jsonb_build_object(
275
+ 'bindings', jsonb_build_array(
276
+ jsonb_build_object('location', 'header', 'name', 'Authorization', 'prefix', 'Bearer ', 'secret_ref', 'primary')
277
+ )
278
+ ),
279
+ 'poll', jsonb_build_object(
280
+ 'bindings', jsonb_build_array(
281
+ jsonb_build_object('location', 'header', 'name', 'Authorization', 'prefix', 'Bearer ', 'secret_ref', 'primary')
282
+ )
283
+ )
284
+ )
285
+ ),
286
+ 'unknown', '{}'
287
+ );
288
+
289
+ -- 2. 注册 Model 行(每个 lane 都需要对应 model)
290
+ INSERT INTO p_ai_models (provider_id, model_id, model_code, capabilities, default_params, param_schema, cost_credits, ...)
291
+ VALUES ((SELECT id FROM p_ai_providers WHERE name='<provider_code>'), '<external_model_id>', '<our_model_code>',
292
+ jsonb_build_object('operations', jsonb_build_array('t2v','i2v')),
293
+ jsonb_build_object('duration', 5),
294
+ jsonb_build_object(...),
295
+ 12, ...);
296
+ ```
297
+
298
+ #### B.2 创建 `00YYYY_seed_<provider>_lane_revisions.up.sql`
299
+
300
+ 按每个 operation 一条 lane + 一条 lane_revision 写入:
301
+
302
+ ```sql
303
+ INSERT INTO p_ai_provider_lanes (provider_id, code, operation, modality, transport_class, status, model_code)
304
+ VALUES ((SELECT id FROM p_ai_providers WHERE name='<provider_code>'),
305
+ '<provider_code>_<model>__<op>__<provider_code>', 'i2v', 'video',
306
+ 'http_json_poll', 'active', '<our_model_code>');
307
+
308
+ INSERT INTO p_ai_provider_lane_revisions (
309
+ lane_id, version,
310
+ endpoint_config, submit_config, poll_config,
311
+ failure_mapper,
312
+ request_defaults, request_mapper, response_normalizer, retry_policy,
313
+ auth_config,
314
+ status
315
+ ) VALUES (
316
+ (SELECT id FROM p_ai_provider_lanes WHERE code = '<...>'),
317
+ 1,
318
+ jsonb_build_object('submit_path', '<submit path>'),
319
+ jsonb_build_object('method', 'POST'),
320
+ jsonb_build_object('method', 'GET', 'path_template', '<poll template, optional>'),
321
+ -- failure_mapper:见 §B.3 模板
322
+ '<failure_mapper json>'::jsonb,
323
+ '{}'::jsonb,
324
+ '{}'::jsonb,
325
+ '{}'::jsonb,
326
+ '{}'::jsonb,
327
+ '{}'::jsonb,
328
+ 'active'
329
+ );
330
+ ```
331
+
332
+ #### B.3 `failure_mapper` 模板
333
+
334
+ ```json
335
+ {
336
+ "rules": [
337
+ {"http_status": [429], "reason_code": "rate_limit"},
338
+ {"provider_error_code": ["<provider 限流字符串 code>"], "reason_code": "rate_limit"},
339
+ {"http_status": [408, 504], "reason_code": "timeout"},
340
+ {"http_status": [500, 502, 503], "reason_code": "provider_error"},
341
+ {"provider_error_code": ["<provider 内容策略 code>"], "reason_code": "output_policy_violation"},
342
+ {"provider_error_code": ["<provider 输入图非法 code>"], "reason_code": "input_image_invalid"}
343
+ ]
344
+ }
345
+ ```
346
+
347
+ > 规则**严格使用 Provider 官方文档明文定义的稳定字符串 code**。**禁止**编造(如 `"rate_limited"` / `"content_policy"` 这类通用名 —— Provider 不一定真的返回这个值)。
348
+ > 如果 Provider 没有稳定字符串 code(如 wavespeed),就只配 `http_status` 规则;adapter 在合适时机合成虚拟 code(参考 wavespeed `nsfw`)。
349
+
350
+ ---
351
+
352
+ ### Phase C: Go Adapter — 实现 `domain.MediaProvider` 接口
353
+
354
+ #### C.1 创建目录与文件
355
+
356
+ ```
357
+ internal/platform/mediagen/infrastructure/providers/<provider>/
358
+ adapter.go
359
+ adapter_test.go
360
+ ```
361
+
362
+ #### C.2 adapter.go 骨架(从 yunwu 或 duomi 复制起)
363
+
364
+ ```go
365
+ package <provider>
366
+
367
+ import (
368
+ "context"
369
+ "fmt"
370
+ "net/http"
371
+ "strings"
372
+ "time"
373
+
374
+ "go.uber.org/zap"
375
+
376
+ "github.com/onex-platform/backend/internal/core/httpclient"
377
+ "github.com/onex-platform/backend/internal/platform/mediagen/domain"
378
+ "github.com/onex-platform/backend/internal/platform/mediagen/infrastructure/providers/base"
379
+ )
380
+
381
+ const (
382
+ providerCode = "<provider>"
383
+ pollEndpoint = "<poll path with %s for external id>" // 仅当 Provider 全平台共享同一 poll 路由
384
+ )
385
+
386
+ type Adapter struct {
387
+ base.Adapter
388
+ }
389
+
390
+ func NewAdapter(logger *zap.Logger, client *httpclient.Client, resolver base.RuntimeConfigResolver) *Adapter {
391
+ return &Adapter{Adapter: base.NewAdapter(providerCode, client, resolver, logger)}
392
+ }
393
+
394
+ func (a *Adapter) Code() string { return providerCode }
395
+
396
+ func (a *Adapter) Capabilities() domain.Capabilities {
397
+ return domain.Capabilities{
398
+ Modalities: []domain.Modality{domain.ModalityVideo},
399
+ Operations: []domain.Operation{domain.OperationTextToVideo, domain.OperationImageToVideo},
400
+ AspectRatios: []string{"9:16", "16:9", "1:1"},
401
+ // 其余字段按 Provider 实际能力填,绝不填 stale defaults
402
+ }
403
+ }
404
+
405
+ // submitResponse / pollResponse:按 Provider 真实响应 schema 定义 typed struct,不要用 map[string]any
406
+ type submitResponse struct { /* ... */ }
407
+ type pollResponse struct { /* ... */ }
408
+ ```
409
+
410
+ #### C.3 Submit 方法
411
+
412
+ ```go
413
+ func (a *Adapter) Submit(ctx context.Context, req *domain.SubmitRequest) (*domain.SubmitResponse, error) {
414
+ if req.Endpoint == "" {
415
+ return nil, fmt.Errorf("<provider> submit: req.Endpoint is empty")
416
+ }
417
+ start := time.Now()
418
+ endpoint, err := a.ResolveEndpoint(ctx, req.Endpoint)
419
+ if err != nil { return nil, fmt.Errorf("<provider> submit resolve endpoint: %w", err) }
420
+ apiKey, err := a.ResolveAPIKey(ctx)
421
+ if err != nil { return nil, fmt.Errorf("<provider> submit resolve api key: %w", err) }
422
+
423
+ headers := map[string]string{"Authorization": "Bearer " + apiKey}
424
+
425
+ var result submitResponse
426
+ resp, err := a.Client.PostWithHeaders(ctx, endpoint, req.Payload, &result, headers)
427
+ duration := time.Since(start)
428
+
429
+ if err != nil {
430
+ a.LogSubmit("submit", req.GenerationID, duration, 0, err)
431
+ return nil, fmt.Errorf("<provider> submit: %w", err)
432
+ }
433
+ if !base.IsSuccessStatus(resp.StatusCode) {
434
+ a.LogSubmit("submit", req.GenerationID, duration, resp.StatusCode, nil)
435
+ return nil, base.MapProviderFailure(base.FailureContext{
436
+ ProviderCode: providerCode,
437
+ Phase: domain.FailurePhaseSubmit,
438
+ HTTPStatus: resp.StatusCode,
439
+ ProviderErrorCode: extractErrorCode(&result, resp.Body), // Provider-specific helper
440
+ Message: base.FirstNonEmpty(result.Error, string(resp.Body)),
441
+ LaneMapper: req.FailureMapper,
442
+ })
443
+ }
444
+ if result.ID == "" {
445
+ return nil, base.MapProviderFailure(base.FailureContext{ /* HTTP 200 但 task_id 缺失 */ })
446
+ }
447
+
448
+ a.LogSubmit("submit", req.GenerationID, duration, resp.StatusCode, nil)
449
+ return &domain.SubmitResponse{
450
+ ExternalID: result.ID,
451
+ Raw: resp.Body,
452
+ Telemetry: &domain.ProviderTelemetry{
453
+ HTTPStatus: int16(resp.StatusCode),
454
+ LatencyMs: duration.Milliseconds(),
455
+ CostUSD: "",
456
+ },
457
+ }, nil
458
+ }
459
+ ```
460
+
461
+ #### C.4 Poll 方法
462
+
463
+ ```go
464
+ func (a *Adapter) Poll(ctx context.Context, req *domain.PollRequest) (*domain.PollResponse, error) {
465
+ externalID := req.ExternalID
466
+ start := time.Now()
467
+ endpoint, err := a.ResolveEndpoint(ctx, fmt.Sprintf(pollEndpoint, externalID))
468
+ // ... HTTP 调用
469
+ if !base.IsSuccessStatus(resp.StatusCode) {
470
+ return nil, base.MapProviderFailure(...)
471
+ }
472
+
473
+ pollResp := &domain.PollResponse{
474
+ Status: mapStatus(result.Status),
475
+ Raw: resp.Body,
476
+ Telemetry: &domain.ProviderTelemetry{...},
477
+ }
478
+ // 终态失败:HTTP 200 但 status=failed / error 字段非空
479
+ if isTerminalFailure(result) {
480
+ provErr := base.MapProviderFailure(base.FailureContext{
481
+ ProviderCode: providerCode,
482
+ Phase: domain.FailurePhasePoll,
483
+ HTTPStatus: resp.StatusCode,
484
+ ProviderErrorCode: result.ErrorCode,
485
+ Message: result.ErrorMessage,
486
+ LaneMapper: req.FailureMapper,
487
+ })
488
+ category := provErr.Category
489
+ reasonCode := provErr.ReasonCode
490
+ pollResp.FailureCategory = &category
491
+ pollResp.FailureReasonCode = &reasonCode
492
+ pollResp.FailureMessage = result.ErrorMessage
493
+ pollResp.Status = domain.StatusFailed // 强制终态 Failed
494
+ }
495
+ if result.OutputURL != "" {
496
+ pollResp.Status = domain.StatusSucceeded
497
+ pollResp.Progress = 1.0
498
+ pollResp.Output = append(pollResp.Output, domain.OutputAsset{Kind: domain.ModalityVideo, URL: result.OutputURL})
499
+ }
500
+ return pollResp, nil
501
+ }
502
+ ```
503
+
504
+ #### C.5 Cancel 方法
505
+
506
+ ```go
507
+ func (a *Adapter) Cancel(ctx context.Context, req *domain.CancelRequest) error {
508
+ return domain.ErrCancelNotSupported // 多数 Provider 不支持
509
+ }
510
+ ```
511
+
512
+ 支持取消的,按 Submit 同样的模式:HTTP 调用 + `MapProviderFailure`。
513
+
514
+ #### C.6 注册到 Wire(`providers/registry.go`)
515
+
516
+ ```go
517
+ import "github.com/onex-platform/backend/internal/platform/mediagen/infrastructure/providers/<provider>"
518
+
519
+ var ProviderSet = wire.NewSet(
520
+ NewRuntimeConfigResolver,
521
+ wavespeed.NewAdapter,
522
+ vidu.NewAdapter,
523
+ wan.NewAdapter,
524
+ duomi.NewAdapter,
525
+ yunwu.NewAdapter,
526
+ pixfree.NewAdapter,
527
+ <provider>.NewAdapter, // 新增
528
+ ProvideAdapterRegistry,
529
+ )
530
+
531
+ func ProvideAdapterRegistry(
532
+ logger *zap.Logger,
533
+ ws *wavespeed.Adapter, vi *vidu.Adapter, wn *wan.Adapter,
534
+ du *duomi.Adapter, yw *yunwu.Adapter, pf *pixfree.Adapter,
535
+ np *<provider>.Adapter, // 新增
536
+ ) (*AdapterRegistry, error) {
537
+ return NewAdapterRegistry(logger, ws, vi, wn, du, yw, pf, np)
538
+ }
539
+ ```
540
+
541
+ > 不需要在 `domain/enum.go` 的 `ProviderCode` 常量列表新增成员(该枚举仅用于编译期类型守卫,目前不强约束所有 Provider;但若 Provider 是面向产品对外曝光的 enum,则同步加常量)。
542
+
543
+ ---
544
+
545
+ ### Phase D: Capability gating + Cost
546
+
547
+ 1. 检查 `p_capability_truth_*` 表是否需要更新(仅当新 Provider 引入了新的 capability gating 规则时;常规接入不需要)。
548
+ 2. `cost_per_call_usd` 与 `cost_credits` 在 `p_ai_models` 写入时已经决定,验证:
549
+ ```sql
550
+ SELECT model_code, cost_credits, cost_per_call_usd FROM p_ai_models WHERE provider_id = ...;
551
+ ```
552
+ 3. 产品曝光策略由仓库外的产品 BFF 负责;OneX 只发布稳定的 Mediagen capability 与 runtime contract,不包含产品 resolver。
553
+
554
+ ---
555
+
556
+ ### Phase E: Credit Deduction
557
+
558
+ Mediagen 模块已经实现 Stripe 风格 authorization → capture 的信用扣费链路(Freeze → Settle / Refund)。**新 Provider 完全继承,无需写额外信用代码**,前提:
559
+
560
+ - Submit 必须发生在 `txManager.WithTransaction(ctx, fn)` 块内(执行 activity 已经如此,无需修改)。
561
+ - Poll 终态 Succeeded → `Settle` 自动触发;终态 Failed/Canceled → `Refund` 自动触发。
562
+ - 这套链路位于 mediagen 的 application/saga 层,对 adapter 透明。
563
+
564
+ ---
565
+
566
+ ### Phase F: 错误处理 — failure mapper 规则
567
+
568
+ - 已经在 Phase B.3 的 lane revision 里写好了基础规则。
569
+ - **新发现的 Provider 错误码**(例如某次线上排障发现 Provider 返回了未文档化的 code):**优先走 admin UI**(无需发版、无需迁移、即时生效):
570
+ 1. 进入 `/ai/runtime/lanes/:laneCode/revisions`
571
+ 2. 在左侧 RevisionTimeline 选择当前 published revision,点击 **"New Revision"**(基于当前版本派生 draft)
572
+ 3. 切到右侧 **Failure** tab,使用 `FailureMapperRuleBuilder` 可视化编辑器(Stripe Radar 风格):
573
+ - 选择匹配维度(`http_status` / `provider_error_code` / `provider_status`)
574
+ - 输入候选值数组(与 §B.3 模板一致:必须是 Provider 文档明文定义的稳定字符串 code)
575
+ - 选择 `reason_code`(dropdown 仅可选 §2.5 9 类合法枚举)
576
+ 4. 点击 **"Dry-run"** → 粘贴一条真实 Provider 响应(例如线上日志里抓到的失败 JSON)→ 验证 `failure_resolution.reason_code` 为预期值
577
+ 5. 点击 **"Publish"** 把 draft revision 设为 active
578
+ - 仍然需要 git-tracked 配置时(如默认 Provider 修订):写一条 `00XXXX_extend_<provider>_failure_mapper.up.sql`,**只动 lane revision 的 failure_mapper JSON**。无论走哪条路径,**绝不改 adapter 代码** —— adapter 的工作只是把 ProviderErrorCode 透传到 `MapProviderFailure`。
579
+ - 服务端兜底:`aiconfig.ValidateLaneRevisionFields` 会在 Save 阶段拒收 `failure_mapper.rules[*].reason_code` 不在 9 类合法值中的 draft(`apierror.BadRequest("lane_revision_invalid", "failure_mapper.rules[N].reason_code")`)。
580
+
581
+ ---
582
+
583
+ ### Phase G: Telemetry + Observability
584
+
585
+ - 已经在 Phase C 的 Submit / Poll 模板里 populated `Telemetry`。
586
+ - Activity 层的 `appendSubmitLog` / `appendPollLog` 把 Telemetry 写入 `p_mediagen_execution_logs`。
587
+ - Admin trace UI 读 `p_mediagen_execution_logs`,按 `generation_id` 关联。
588
+ - 可观测性自动接入,不需要额外配置。
589
+
590
+ ---
591
+
592
+ ### Phase H: Tests
593
+
594
+ #### H.1 Adapter unit tests(`<provider>/adapter_test.go`)
595
+
596
+ 最小覆盖面(每条都必须存在):
597
+
598
+ | 用例 | 断言 |
599
+ |------|------|
600
+ | Submit 成功 | 返回 SubmitResponse;ExternalID 非空;Telemetry.HTTPStatus / LatencyMs 已填 |
601
+ | Submit HTTP 4xx 失败 | 返回 `*ProviderError`;ReasonCode 来自 mapper rule |
602
+ | Submit HTTP 5xx 失败 | 返回 `*ProviderError`;ReasonCode = `provider_error`;Retryable=true |
603
+ | Submit HTTP 200 但 task_id 缺失 | 返回 `*ProviderError`;不 panic |
604
+ | Poll 成功(异步终态 Succeeded) | Status=Succeeded;Output 非空 |
605
+ | Poll 终态失败(HTTP 200 + status=failed) | Status=Failed;FailureReasonCode 已填 |
606
+ | Poll HTTP 4xx | 返回 `*ProviderError` |
607
+ | `mapStatus` 全枚举映射 | 表驱动测试覆盖 Provider 所有 status 字面量 |
608
+ | Cancel 不支持 | 返回 `domain.ErrCancelNotSupported` |
609
+ | Cancel 支持(如适用) | 成功 + 失败两条路径 |
610
+
611
+ 测试**必须**用真实响应样本作为 fixture(从 Provider 文档复制粘贴),不允许 mock 一切。
612
+
613
+ #### H.2 Integration test(仅当 transport 不是常规 HTTP)
614
+
615
+ 参考 `infrastructure/temporal/activity/runtime_execution_activities_test.go`。pixfree-class 的非 HTTP transport 必须有 integration test。
616
+
617
+ ---
618
+
619
+ ### Phase I: Wire generation
620
+
621
+ ```bash
622
+ cd "$(git rev-parse --show-toplevel)/backend" && make wire 2>&1 | tail
623
+ ```
624
+
625
+ 期望输出:`Wire has generated wire_gen.go for ...`。失败必修。
626
+
627
+ ---
628
+
629
+ ### Phase J: Verification
630
+
631
+ ```bash
632
+ cd "$(git rev-parse --show-toplevel)/backend" && \
633
+ go vet ./... && \
634
+ go build ./... && \
635
+ go test ./internal/platform/mediagen/... 2>&1 | tail
636
+ ```
637
+
638
+ 三条命令**必须全绿**。`go test` 覆盖 adapter unit tests。
639
+
640
+ ---
641
+
642
+ ### Phase K: Migration apply(dev DB)
643
+
644
+ > 仅当走 §B-Migration 路径时执行;走 §B-UI 路径直接跳到 §L。
645
+
646
+ ```bash
647
+ cd "$(git rev-parse --show-toplevel)"
648
+ compose=(docker compose --env-file deployments/.env.dev \
649
+ -f deployments/docker-compose.yml \
650
+ -f deployments/docker-compose.dev.yml)
651
+ "${compose[@]}" stop --timeout 30 \
652
+ platform-api platform-api-2 admin-api admin-api-2 \
653
+ documentation-api documentation-api-2 worker
654
+ "${compose[@]}" run --rm --no-deps bootstrap-database bootstrap
655
+ "${compose[@]}" run --rm --no-deps set-db-passwords
656
+ "${compose[@]}" run --rm --no-deps migrate up
657
+ "${compose[@]}" run --rm --no-deps grant-runtime-access
658
+ "${compose[@]}" up -d platform-api admin-api documentation-api worker
659
+ ```
660
+
661
+ 期望:迁移成功执行,`schema_migrations.dirty = false`。
662
+
663
+ ---
664
+
665
+ ### Phase L: Admin Onboarding Click-Through(7 步全程在 admin UI)
666
+
667
+ 接入新 Provider 的最后一步是**让运营在 admin UI 验证全流程可用**。即使走了 §B-Migration 路径,下面的可视化校验仍然必须勾选 —— 它是 admin 与后端契约一致性的活体检查。
668
+
669
+ | Step | UI 入口 | 操作 | 验收信号 |
670
+ |------|---------|------|---------|
671
+ | 1. 创建 Provider | `/ai/runtime/providers` → **"Create Provider"** 抽屉 | 填写 `name` / `display_name` / `base_url` / `api_key`(写到 secret_bundle);展开 **Auth Profiles** 区,使用 `AuthProfileBindingEditor` 添加 submit / poll 两组绑定(每条 row:`location`, `name`, `value_prefix`, `secret_ref`) | 列表出现新行,状态标签 `active`(绿色) |
672
+ | 2. 注册 Model | `/ai/control-plane/models` → **"Create Model"** | 选择刚才的 Provider,填 `model_id`(外部 id)/ `model_code`(我方稳定 code)/ `capabilities.operations` 数组 / `cost_credits`。该页面是 LLM 与媒体生成模型的统一注册入口,列表顶栏 `provider_type` 下拉过滤切换 LLM / media 域 | 列表筛 `provider_type=media` 后看到新行;按 `provider_id` 二次过滤可定位 |
673
+ | 3. 创建 Lane | `/ai/runtime/lanes` → **"Create Lane"** 抽屉 | 填 `code` / `operation` / `modality` / `transport_class` / `provider_id` / `model_code`,与 §1.3 命名规范保持一致:`<provider>_<model>__<op>__<provider>` | 列表出现新 lane,`published_revision_no` 暂为空 |
674
+ | 4. 创建 Lane Revision | `/ai/runtime/lanes/:laneCode/revisions` → **"Create Revision"**(顶栏右侧) | 在右侧 11-tab 编辑器逐 tab 填写:Endpoints / Auth / Submit / Poll / Response / Failure / Request Mapper / Reliability / Telemetry / Diff / Dry-run。可视化编辑器(`FailureMapperRuleBuilder`、`RequestMapperEditor`、`ResponseNormalizerEditor`、`RetryPolicyEditor`)兜底,运营**不需要写 JSON** | 点击 **"Save Draft"** 后,左侧 RevisionTimeline 顶部出现新 draft 行;服务端通过 `ValidateLaneRevisionFields`(12 字段全过) |
675
+ | 5. Dry-run 验证 | 同页面 → 顶栏 **"Dry-run"** 按钮(打开 `LaneRevisionDryRunDrawer`) | 粘贴一份样本输入(参考 Phase A 研究里收集的 Provider 文档示例),运行 | 抽屉返回 `normalized_request`(被 request_mapper 应用后的最终 payload)+ `applied_transforms` 列表;点击 **"Copy as cURL"** 可直接 paste 到终端复现请求 |
676
+ | 6. Publish | 同页面 → 顶栏 **"Publish"** 按钮(打开 `LaneRevisionPublishDialog`) | 确认 modal | Lane.`published_revision_id` 翻新;该 lane 下次 Generation 立即使用新 revision |
677
+ | 7. Rollback(仅故障时) | 同页面 → 选中"上一个 published" 行 → 顶栏 **"Rollback"** | 确认 modal(`POST /admin/ai/provider-lanes/:id/revisions/:revisionId/rollback`,权限 `ai_lane:rollback`) | 上一版本恢复为 active;当前 broken revision 标记 `superseded`;服务端兜底:若不存在历史 published revision,rollback 直接返回 `apierror.Conflict` |
678
+
679
+ > 全流程下来一个新 Provider 不需要再回 SQL 或后端代码 —— 只在 §C / §H 出现的 Go adapter 是真正需要写代码的部分;其他所有"配置化"工作都在 admin UI 完成。
680
+
681
+ ---
682
+
683
+ ## Section 4: Reference Implementation Map
684
+
685
+ 新 Provider 类型 → 参考已有 adapter 复制:
686
+
687
+ | 新 Provider 类型 | 参考实现 | 适用原因 |
688
+ |------------------|---------|---------|
689
+ | HTTP 异步 + 状态字段 + 字符串 error code | `vidu/adapter.go` | post-alignment 最干净的实现,演示 `{code,reason,message}` 信封解析 + 字符串 reason 路由 |
690
+ | HTTP 异步 + 嵌套响应(`output.*`) | `wan/adapter.go` | Dashscope 模式:`output.task_id` / `output.task_status` / `output.code`,演示嵌套 envelope 解析 |
691
+ | HTTP 同步(图像生成立即返回) | `duomi/adapter.go`(nano-banana 分支) | 演示 `sync:<base64>` ExternalID 短路:Submit 返回的"任务 id"实际上携带最终结果,Poll 识别前缀直接 Succeeded |
692
+ | 浏览器自动化 / 非 HTTP | `pixfree/adapter.go` | 演示 Temporal stealthActivity 委派模式 |
693
+ | 内容审核是 success 中的 flag(非 error code) | `wavespeed/adapter.go` | 演示 `has_nsfw_contents[]` 后置探测 + 合成 `nsfw` provider_error_code |
694
+ | Bearer auth + 自定义 header | `wan/adapter.go`(`X-DashScope-Async`) | 演示 Provider 强制必填的扩展 header |
695
+ | Token auth(字面量 `Token ` 前缀) | `vidu/adapter.go` | 演示 auth scheme 不是 Bearer 也不是裸 token 的常量 prefix |
696
+ | Query string auth(`?key=`) | `duomi/adapter.go` | 演示通过 lane `auth_config.location: query` 让 base 适配器自动 inject |
697
+ | HTTP 200 + `error` 字段表达业务失败 | `yunwu/adapter.go` | 演示强制 `Status=Failed` 的 invariant(避免 poll loop 吞噬终态错误) |
698
+
699
+ ---
700
+
701
+ ## Section 5: 验收 Checklist(Pre-PR)
702
+
703
+ 提 PR 前**逐条勾选**。任何一条没勾,禁止 PR。
704
+
705
+ - [ ] `go vet ./...` 通过
706
+ - [ ] `go build ./...` 通过
707
+ - [ ] `go test ./internal/platform/mediagen/...` 通过
708
+ - [ ] 迁移在 dev DB apply 成功(`schema_migrations.dirty = false`)
709
+ - [ ] 没有 `// TODO` / `// FIXME` / `// 暂` / `// 后续` 注释(D1)
710
+ - [ ] 没有 `strings.Contains` 做错误分类(§2.2)
711
+ - [ ] 没有 `gin.H` 出现在 adapter(R1,本来 adapter 就不应该有)
712
+ - [ ] 所有 `map[string]any` 字段 / 参数都有 `//nolint:T1` 说明(T1)
713
+ - [ ] 每次 `base.MapProviderFailure` 调用都传了 `req.FailureMapper`(§2.4)
714
+ - [ ] `ProviderErrorCode` 用的是**官方文档明文定义的字符串 code**(不是数字字符串、不是编造名)
715
+ - [ ] lane revision 的 `failure_mapper` JSON 列出了 Provider 文档中所有错误码组(按 §B.3 模板)
716
+ - [ ] Submit / Poll 成功路径填充了 `ProviderTelemetry` 全部 4 字段
717
+ - [ ] Submit / Poll 失败路径**不**填 Telemetry(让 `*ProviderError` 携带 StatusCode)
718
+ - [ ] adapter 没有 hardcode submit 端点常量(§2.3)
719
+ - [ ] `Capabilities()` 反映 Provider 真实模型目录(不是 stale defaults)
720
+ - [ ] adapter unit test 覆盖 §H.1 全部 10 项最小用例
721
+ - [ ] `make wire` 重新生成且无 diff 残留
722
+ - [ ] `/self-review` 跑过(如果是 Level B 设计)
723
+
724
+ #### Admin UI 验收(§L 走完之后)
725
+
726
+ - [ ] `/ai/runtime/providers` 列出新 Provider,状态标签为 `active`(绿色)
727
+ - [ ] `/ai/runtime/lanes` 列出新 lane,`published_revision_no` 显示具体版本号(不为空)
728
+ - [ ] `/ai/runtime/lanes/:laneCode/revisions` 工作台正常打开(顶部 context strip 显示 Provider / Lane / Published rev;左侧 RevisionTimeline 倒序排列;右侧 11-tab 编辑器全部加载无报错)
729
+ - [ ] Dry-run 抽屉用样本输入返回合理 `normalized_request` + `applied_transforms`,"Copy as cURL" 可一键复用
730
+ - [ ] Publish 后 Lane.`published_revision_id` 翻新;Rollback 后恢复上一 published revision
731
+ - [ ] `/ai/runtime/generations` 用新 lane 跑一次真实 Generation,状态最终落到 `succeeded` / `failed`(非 `pending` 卡住)
732
+ - [ ] `/ai/runtime/execution-logs` 顶级搜索按 `lane_code` 过滤可见对应 phase(submit / poll)日志,request / response snapshot 可侧拉展开
733
+
734
+ ---
735
+
736
+ ## Section 6: Common Pitfalls(lessons learned)
737
+
738
+ post-alignment 过程踩到的 5 个最大坑——新 Provider 接入时**主动避开**:
739
+
740
+ ### Pitfall 1: 自创 reason code
741
+
742
+ **症状**:在 lane mapper 里写 `"reason_code": "credit_insufficient"` / `"safety_violation"` / `"output_invalid"`。
743
+ **问题**:不在 `domain.FailureReasonCode` 9 类中,`ResolveFailureReasonCode` 不识别,最终落到默认分支 = `provider_error`。
744
+ **正确**:只用 9 类合法值。Auth/billing/bad_input 类别全部映射到 `provider_error`(HTTP 4xx 不可重试)。新增类别需要 domain 层改造,不在单 Provider 接入范围。
745
+
746
+ ### Pitfall 2: 文案匹配错误分类
747
+
748
+ **症状**:`if strings.Contains(message, "rate limit") { return rateLimit }`。
749
+ **问题**:Provider 文案 i18n / 改版 / 不同 endpoint 文案不一致 → 系统行为漂移 → 排障时要去 grep 哪些字符串还在用。
750
+ **正确**:100% 走结构化信号——sentinel error、typed `ApplicationError`、JSON envelope code。文案只用于 logging。
751
+
752
+ ### Pitfall 3: `LaneMapper: nil`
753
+
754
+ **症状**:`base.MapProviderFailure(base.FailureContext{ ... /* 没传 LaneMapper */ })`。
755
+ **问题**:lane snapshot 里精心配的 mapper rules 全部失效,Provider 错误一律落到默认分支。
756
+ **正确**:每个 `MapProviderFailure` 调用必须 `LaneMapper: req.FailureMapper`。Code review 时 grep 这个 pattern。
757
+
758
+ ### Pitfall 4: Adapter hardcode submit endpoint
759
+
760
+ **症状**:`const submitEndpoint = "/v1/predictions"` + `req.Endpoint` 字段被忽略。
761
+ **问题**:lane revision 的 `endpoint_config.submit_path` 形同虚设;不同 lane 用同一 endpoint;DB 改路径不生效。
762
+ **正确**:Submit 入口校验 `req.Endpoint != ""`;adapter 不存在 `submitEndpoint` 常量。Poll endpoint 仅当全 Provider 共享同一路由时允许 const(如 wan / wavespeed)。
763
+
764
+ ### Pitfall 5: status 与 error 字段独立处理
765
+
766
+ **症状**:HTTP 200 + body `{"status":"processing","error":"NSFW detected"}` —— adapter 看 `status="processing"` 就返回 `Processing`,丢了 `error`。
767
+ **问题**:activity poll loop 会一直轮询直到超时,吞噬掉了终态错误信号。
768
+ **正确**:error/reason 字段非空时**强制** `Status=Failed`(参考 yunwu adapter 的 `pollResp.Status = domain.StatusFailed` + 注释引用 Stripe/OpenAI/Bedrock 行业惯例)。
769
+
770
+ ---
771
+
772
+ ## Section 8: 新增 Admin 功能(admin-integrated 阶段)
773
+
774
+ > 适用提交:`fc1ee8d6`(admin 配置面板首发)/ `68fb828b`(rollback / diff / dry-run + JSON Schema)/ `b4d77656`(cross-generation execution log)/ `bb9e44a6`(permission seed + enum)/ `211ed024`(DTO snake_case 锁定)。
775
+
776
+ ### 8.1 Lane Revision 编排工作台(核心 XL 页面)
777
+
778
+ **路由**:`/ai/runtime/lanes/:laneCode/revisions`
779
+
780
+ **布局**:
781
+ - **顶部 Context Strip**:固定显示当前 Provider / Lane / Published revision number,导航不丢失上下文
782
+ - **左侧 RevisionTimeline**:vertical timeline,倒序排列所有 revision;每条带状态徽章(`draft` / `published` / `superseded` / `rolled_back`);点击切换右侧编辑器内容
783
+ - **右侧 11-tab 编辑器**:Endpoints / Auth / Submit / Poll / Response / Failure / Request Mapper / Reliability / Telemetry / Diff / Dry-run
784
+ - **右上 Action Bar**:Save Draft / Publish / Rollback / New Revision / Dry-run
785
+
786
+ **为什么是工作台而不是表单**:lane revision 跨 12 个 JSONB 字段,互相之间有强语义耦合(如 `failure_mapper.rules.reason_code` 必须 ⊂ `retry_policy.retryable_codes` 白名单 + §2.5 9 类)。把它们集中到一个屏幕、配合 Diff 与 Dry-run 即时验证,是行业惯例(参考 AWS Step Functions Workflow Studio、Stripe Workbench)。
787
+
788
+ ### 8.2 9 个可视化编辑器组件(components/platform/)
789
+
790
+ | 组件 | 对应字段 | 风格参考 |
791
+ |------|---------|---------|
792
+ | `AuthProfileBindingEditor` | Provider.`auth_profiles.{submit,poll}.bindings[]` | AWS IAM Role Policy 行级编辑:`location` / `name` / `value_prefix` / `secret_ref` 四列 |
793
+ | `FailureMapperRuleBuilder` | LaneRevision.`failure_mapper.rules[]` | Stripe Radar 规则编辑:`when http_status / provider_error_code / provider_status IN [...] THEN reason_code` |
794
+ | `RequestMapperEditor` | LaneRevision.`request_mapper.transforms[]` | `from_path` → `to_path` + transform 列;JSONPath 输入即时校验 |
795
+ | `ResponseNormalizerEditor` | LaneRevision.`response_normalizer.paths` | `status` / `output` / `error` 三个 JSONPath;右侧 sample test pane 粘贴一段响应即时验证抽取结果 |
796
+ | `RetryPolicyEditor` | LaneRevision.`retry_policy` | ECharts log-scale backoff 曲线可视化(max_attempts / initial / multiplier / cap 四参数即时联动) |
797
+ | `RevisionTimeline` | 全部 revisions | 垂直时间轴 + 状态徽章 |
798
+ | `RevisionDiffViewer` | 任意两 revision | Monaco editor diff 视图(左 base / 右 compare) |
799
+ | `LaneRevisionPublishDialog` | publish 操作 | 确认 modal,列出当前 vs draft 差异要点 |
800
+ | `LaneRevisionDryRunDrawer` | dry-run 操作 | 抽屉表单:sample input → normalized_request + applied_transforms + cURL one-click copy |
801
+
802
+ ### 8.3 Dry-run 流程(不调用 Provider 不写库)
803
+
804
+ ```
805
+ Operator 在工作台填好 draft → 点击 Dry-run
806
+
807
+ admin-api: POST /admin/ai/provider-lanes/:id/revisions/:revisionId/dry-run
808
+ 权限: ai_lane:dry_run(与 write / publish 解耦,运营可独立授权)
809
+
810
+ Service: 构造一个虚拟 SubmitRequest (sample input + draft revision snapshot)
811
+ → 跑 request_mapper transforms
812
+ → 跑 auth_config bindings 注入
813
+ → 跑 endpoint_config submit_path 替换
814
+ → **不发起真实 HTTP / 不写 p_mediagen_execution_logs**
815
+
816
+ 返回: { normalized_request, applied_transforms[], curl_template, validation_warnings[] }
817
+ ```
818
+
819
+ 行业对齐:AWS Step Functions Workflow Studio "Test State"、Stripe Workbench "Simulator"。dry-run 是只读语义,因此走独立 `ai_lane:dry_run` 权限码(commit `bb9e44a6` 迁移 484 已注入到 admin / super_admin role)。
820
+
821
+ ### 8.4 Diff between revisions
822
+
823
+ `GET /admin/ai/provider-lanes/:id/revisions/:revisionId/diff?against=:other`(权限 `ai_lane:read`)
824
+
825
+ 返回结构(与前端 DTO snake_case 对齐):
826
+ ```json
827
+ {
828
+ "left_revision_id": 42,
829
+ "right_revision_id": 41,
830
+ "changes": {
831
+ "failure_mapper.rules[2].reason_code": { "before": "provider_error", "after": "rate_limit" },
832
+ "retry_policy.max_attempts": { "before": 3, "after": 5 }
833
+ }
834
+ }
835
+ ```
836
+
837
+ 工作台 Diff tab 内嵌 `RevisionDiffViewer`(Monaco)展示 left/right 两份原始 JSON 的差异,配合 `changes` 字段做高亮跳转。
838
+
839
+ ### 8.5 Rollback 安全护栏
840
+
841
+ - 端点:`POST /admin/ai/provider-lanes/:id/revisions/:revisionId/rollback`(权限 `ai_lane:rollback`)
842
+ - 服务端先校验:当前 lane 必须存在**至少一条** prior `published` revision,否则返回 `apierror.Conflict("rollback_requires_prior_published")`
843
+ - 触发后:`previous published revision`(按 `published_at` 倒数第二条)变回 `published` 状态,当前 active revision 标记 `rolled_back`
844
+ - 审计:通过 DomainEvent envelope `AuditChanges` 写入 `sys_audit_logs`(不走 `audit.Enrich()`,遵循 CloudTrail Envelope Pattern)
845
+
846
+ ### 8.6 Cross-generation Execution Log Search
847
+
848
+ **路由**:`/ai/runtime/execution-logs`(顶级,非 generation 详情子页)
849
+ **端点**:`GET /admin/v1/mediagen/execution-logs`(权限 `mediagen:execution_log:read`)
850
+
851
+ 排障驱动:当某条 lane 在线上突然失败率上升,SRE 不需要先逐条 generation 点进去查 —— 直接用 `lane_code` + `phase`(submit / poll)+ `outcome`(success / failure)+ 时间窗口在顶级搜索,命中后侧拉展开 request / response snapshot。
852
+
853
+ 支撑迁移:`000483_extend_p_mediagen_execution_logs_phase_lane.up.sql` 给 `p_mediagen_execution_logs` 加了:
854
+ - `phase VARCHAR(20)` 列(值域:`submit` / `poll` / `cancel`,对应 `DomainMediagenExecutionLogPhase` 枚举)
855
+ - `lane_code VARCHAR(120)` 列(冗余 lane.code,避免 join)
856
+ - `outcome VARCHAR(20)` 列(对应 `DomainMediagenExecutionOutcome` 枚举)
857
+ - 复合索引 `(started_at DESC, lane_code)`,命中"按时间倒序看某 lane 全部日志"的高频查询模式
858
+
859
+ ### 8.7 Provider / Model Toggle
860
+
861
+ - `POST /admin/ai/providers/:id/toggle`(权限 `ai_provider:write`)
862
+ - `POST /admin/ai/models/:id/toggle`(权限 `ai_model:write`)
863
+
864
+ 行级灰度开关,比 Update 接口更安全(toggle 不接受任何其他字段,只翻 `status` 一列),与 GitHub `enable/disable` workflow 风格对齐。
865
+
866
+ ### 8.8 服务端 JSON Schema 强校验
867
+
868
+ 见 §2.9 不变量 I-9。Save Draft / Publish 都走 `ValidateLaneRevisionFields`,bad config 在 admin UI 立即返回 `apierror.BadRequest("lane_revision_invalid", "<dot.path>")`,前端按 `path` 在对应 tab 高亮报错字段。**这是 D7(zero-tolerance for stub config)的工程化兜底**。
869
+
870
+ ---
871
+
872
+ ## Section 7: 参考资料
873
+
874
+ - `internal/platform/mediagen/domain/provider_request.go` —— SubmitRequest / PollRequest / CancelRequest / ProviderTelemetry 真实定义
875
+ - `internal/platform/mediagen/domain/failure_reason.go` —— FailureReasonCode 9 类枚举与 ResolveFailureReasonCode
876
+ - `internal/platform/mediagen/infrastructure/providers/base/failure_mapping.go` —— MapProviderFailure 实现
877
+ - `internal/platform/mediagen/infrastructure/providers/base/adapter.go` —— Adapter 基类
878
+ - `internal/platform/mediagen/infrastructure/providers/registry.go` —— Wire ProviderSet 注册点
879
+ - `internal/platform/ai/aiconfig/lane_revision_validator.go` —— 12 JSONB 字段服务端校验(§2.9 / I-9)
880
+ - `internal/platform/ai/aiconfig/router.go` —— `/admin/ai/providers/*` + `/admin/ai/provider-lanes/*` 路由 + RBAC 绑定
881
+ - `internal/platform/mediagen/interfaces/adminapi/routes.go` —— `/admin/v1/mediagen/generations` + `/execution-logs` 路由
882
+ - `admin/apps/web-antd/src/views/ai/runtime/lanes/[code]/revisions/index.vue` —— Lane Revision 工作台 XL 页面
883
+ - `admin/apps/web-antd/src/components/platform/` —— 9 个可视化编辑器组件(§8.2)
884
+ - `admin/apps/web-antd/src/constants/permission-codes.ts` —— `MEDIAGEN_*` 权限码常量
885
+ - `migrations/000442_create_p_ai_provider_lanes.up.sql` —— lanes / lane_revisions DDL
886
+ - `migrations/000445_hard_cut_runtime_profile_steps_to_lane_refs.up.sql` —— bulk lane seed
887
+ - `migrations/000472_seed_provider_lane_failure_mapper.up.sql` —— failure_mapper backfill
888
+ - `migrations/000473_add_generation_transport_retry_count.up.sql` —— transport_retry_count 列
889
+ - `migrations/000483_extend_p_mediagen_execution_logs_phase_lane.up.sql` —— execution_log phase + lane_code + 复合索引
890
+ - `migrations/000484_seed_ai_lane_dry_run_permission.up.sql` —— `ai_lane:rollback` + `ai_lane:dry_run` 权限种子
891
+ - 短表速查:`backend/.claude/docs/mediagen-provider-quick-reference.md`
892
+
893
+ ---
894
+
895
+ ## Appendix A: 提交参考链
896
+
897
+ post-alignment 与 admin-integrated 两个阶段的关键提交(按时间倒序,最新 → 最旧):
898
+
899
+ | Commit | 说明 |
900
+ |--------|-----|
901
+ | `211ed024` | admin mediagen 合规清理 + Provider/Lane/LaneRevision DTO snake_case 与后端对齐 |
902
+ | `bb9e44a6` | 注入 `ai_lane:rollback` 权限 + 注册 `DomainMediagenExecutionLogPhase` / `DomainMediagenExecutionOutcome` 枚举 |
903
+ | `fc1ee8d6` | admin Provider / Lane / Lane-Revision 配置面板首发(UI 全流程) |
904
+ | `b4d77656` | mediagen admin cross-generation execution log 搜索 + 可观测性 schema 扩展 |
905
+ | `68fb828b` | aiconfig 增加 lane revision rollback / diff / dry-run + JSON Schema 校验 + toggle 过滤 |
906
+ | `7526a1ed` | mediagen Provider Onboarding SOP 首版 + capability matrix |
907
+ | `3ba754f7` | dedupe `firstNonEmpty` + `statusCodeOrDefault` 到 `providers/base/util` |
908
+ | `84953e39` | wan adapter 重写为 Alibaba Dashscope 规范 |
909
+ | `4d910338` | vidu adapter 对齐 Shengshu Vidu Studio 官方 API |
910
+ | `9e621fc8` | wavespeed adapter 对齐官方 API + NSFW 后置探测 |
911
+ | `60bf4b09` | duomi adapter 对齐官方 API(doc/26 / 52 / 85 / 86) |
912
+ | `a0374e88` | 清理 fal / replicate / runway 三个未使用的 Provider |
913
+ | `f2338a6d` | Stripe 风格 Provider failure mapping 标准化(post-alignment 起点) |
914
+
915
+ ---
916
+
917
+ > **本 SOP 是 mediagen Provider 接入的唯一权威流程**。任何脱离本流程的快速接入都会导致:lane snapshot 与 adapter 行为不一致、failure_mapper 不生效、telemetry 缺失、credit 计费链路受影响。整个 alignment 批次(commits `f2338a6d` → `211ed024`)的工作量证明了这一点。