pz-cli 5.0.0__py3-none-any.whl

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 (399) hide show
  1. pz_cli-5.0.0.dist-info/METADATA +51 -0
  2. pz_cli-5.0.0.dist-info/RECORD +399 -0
  3. pz_cli-5.0.0.dist-info/WHEEL +4 -0
  4. pz_cli-5.0.0.dist-info/entry_points.txt +5 -0
  5. pz_core/__init__.py +64 -0
  6. pz_core/__main__.py +7 -0
  7. pz_core/app.py +45 -0
  8. pz_core/assembly/__init__.py +17 -0
  9. pz_core/assembly/app_discovery.py +1240 -0
  10. pz_core/assembly/apps/__init__.py +25 -0
  11. pz_core/assembly/apps/compiler.py +824 -0
  12. pz_core/assembly/apps/providers.py +188 -0
  13. pz_core/assembly/cli/__init__.py +9 -0
  14. pz_core/assembly/cli/compiler.py +166 -0
  15. pz_core/assembly/diagnostics/__init__.py +21 -0
  16. pz_core/assembly/diagnostics/checks.py +257 -0
  17. pz_core/assembly/diagnostics/dev_sources.py +569 -0
  18. pz_core/assembly/events/__init__.py +5 -0
  19. pz_core/assembly/events/compiler.py +219 -0
  20. pz_core/assembly/extensions/__init__.py +5 -0
  21. pz_core/assembly/extensions/compiler.py +696 -0
  22. pz_core/assembly/infra/__init__.py +6 -0
  23. pz_core/assembly/infra/compiler.py +272 -0
  24. pz_core/assembly/lifecycle/__init__.py +5 -0
  25. pz_core/assembly/lifecycle/compiler.py +95 -0
  26. pz_core/assembly/middlewares/__init__.py +19 -0
  27. pz_core/assembly/middlewares/compiler.py +508 -0
  28. pz_core/assembly/project/__init__.py +7 -0
  29. pz_core/assembly/project/compiler.py +9 -0
  30. pz_core/assembly/project/context.py +17 -0
  31. pz_core/assembly/project/discovery.py +227 -0
  32. pz_core/assembly/resources/__init__.py +19 -0
  33. pz_core/assembly/resources/compiler.py +310 -0
  34. pz_core/assembly/resources/settings.py +129 -0
  35. pz_core/assembly/routes/__init__.py +7 -0
  36. pz_core/assembly/routes/compiler.py +400 -0
  37. pz_core/assembly/runtime_manifests/__init__.py +13 -0
  38. pz_core/assembly/runtime_manifests/compiler.py +159 -0
  39. pz_core/assembly/settings/__init__.py +17 -0
  40. pz_core/assembly/settings/compiler.py +786 -0
  41. pz_core/assembly/settings/host_project_overrides.py +82 -0
  42. pz_core/assembly/settings/host_runtime_endpoint.py +207 -0
  43. pz_core/authoring/__init__.py +79 -0
  44. pz_core/authoring/app_init_service.py +946 -0
  45. pz_core/authoring/app_versions.py +814 -0
  46. pz_core/authoring/cli.py +192 -0
  47. pz_core/authoring/commands.py +1810 -0
  48. pz_core/authoring/conflicts.py +180 -0
  49. pz_core/authoring/dev_bindings.py +916 -0
  50. pz_core/authoring/dev_git.py +1087 -0
  51. pz_core/authoring/dev_import.py +1061 -0
  52. pz_core/authoring/dev_manifest.py +522 -0
  53. pz_core/authoring/dev_sources.py +1638 -0
  54. pz_core/authoring/env_cli.py +454 -0
  55. pz_core/authoring/files.py +109 -0
  56. pz_core/authoring/ide_projection.py +1129 -0
  57. pz_core/authoring/naming.py +58 -0
  58. pz_core/authoring/official_dev_sources.py +432 -0
  59. pz_core/authoring/official_sources.json +156 -0
  60. pz_core/authoring/orchestration_service.py +558 -0
  61. pz_core/authoring/project_default.jsonc +53 -0
  62. pz_core/authoring/project_defaults.py +88 -0
  63. pz_core/authoring/project_metadata_service.py +199 -0
  64. pz_core/authoring/project_settings_checks.py +51 -0
  65. pz_core/authoring/project_settings_cli.py +299 -0
  66. pz_core/authoring/project_settings_service.py +1700 -0
  67. pz_core/authoring/project_state.py +399 -0
  68. pz_core/authoring/publish_git.py +423 -0
  69. pz_core/authoring/publish_lock.py +188 -0
  70. pz_core/authoring/publish_source.py +195 -0
  71. pz_core/authoring/release.py +1382 -0
  72. pz_core/authoring/repo_keys.py +29 -0
  73. pz_core/authoring/root_cli.py +28 -0
  74. pz_core/authoring/scaffold_service.py +325 -0
  75. pz_core/authoring/sync_service.py +787 -0
  76. pz_core/authoring/templates/app/__init__.py +1 -0
  77. pz_core/authoring/templates/app/api.py +25 -0
  78. pz_core/authoring/templates/app/apps.py +31 -0
  79. pz_core/authoring/templates/app/auth_package_init.py +14 -0
  80. pz_core/authoring/templates/app/auth_readme.py +29 -0
  81. pz_core/authoring/templates/app/configs_package_init.py +14 -0
  82. pz_core/authoring/templates/app/default_env.py +18 -0
  83. pz_core/authoring/templates/app/default_jsonc.py +25 -0
  84. pz_core/authoring/templates/app/locales_readme.py +25 -0
  85. pz_core/authoring/templates/app/migrations_readme.py +19 -0
  86. pz_core/authoring/templates/app/models.py +14 -0
  87. pz_core/authoring/templates/app/package_init.py +8 -0
  88. pz_core/authoring/templates/app/pages_base.py +161 -0
  89. pz_core/authoring/templates/app/pages_readme.py +25 -0
  90. pz_core/authoring/templates/app/public.py +31 -0
  91. pz_core/authoring/templates/app/pyproject.py +77 -0
  92. pz_core/authoring/templates/app/readme.py +74 -0
  93. pz_core/authoring/templates/app/settings.py +46 -0
  94. pz_core/authoring/templates/app/static_app_js.py +24 -0
  95. pz_core/authoring/templates/app/static_readme.py +31 -0
  96. pz_core/authoring/templates/app/template_config_page.py +23 -0
  97. pz_core/authoring/templates/app/template_index_html.py +28 -0
  98. pz_core/authoring/templates/app/templates_readme.py +32 -0
  99. pz_core/authoring/templates/app/urls.py +36 -0
  100. pz_core/authoring/templates/app/views.py +36 -0
  101. pz_core/authoring/templates/project/__init__.py +1 -0
  102. pz_core/authoring/templates/project/agents.py +51 -0
  103. pz_core/authoring/templates/project/apps_container_readme.py +37 -0
  104. pz_core/authoring/templates/project/main_py.py +59 -0
  105. pz_core/authoring/templates/project/project_env.py +19 -0
  106. pz_core/authoring/templates/project/pyproject.py +26 -0
  107. pz_core/authoring/templates/project/pz_env_env.py +65 -0
  108. pz_core/authoring/templates/project/pz_env_jsonc.py +69 -0
  109. pz_core/authoring/templates/project/pz_jsonc.py +12 -0
  110. pz_core/authoring/templates/project/readme.py +94 -0
  111. pz_core/authoring/templates/project/root_gitkeep.py +8 -0
  112. pz_core/authoring/templates/shared/__init__.py +5 -0
  113. pz_core/authoring/templates/shared/text.py +10 -0
  114. pz_core/authoring/venv_service.py +87 -0
  115. pz_core/authoring/workspace.py +167 -0
  116. pz_core/cli.py +291 -0
  117. pz_core/cli_options.py +74 -0
  118. pz_core/core/__init__.py +3 -0
  119. pz_core/core/errors/__init__.py +5 -0
  120. pz_core/core/errors/models.py +117 -0
  121. pz_core/core/events/__init__.py +5 -0
  122. pz_core/core/events/registry.py +54 -0
  123. pz_core/core/extensions/__init__.py +51 -0
  124. pz_core/core/extensions/registry.py +790 -0
  125. pz_core/core/health/__init__.py +6 -0
  126. pz_core/core/health/contracts.py +10 -0
  127. pz_core/core/middlewares/__init__.py +6 -0
  128. pz_core/core/middlewares/models.py +24 -0
  129. pz_core/core/routing/__init__.py +5 -0
  130. pz_core/core/routing/models.py +21 -0
  131. pz_core/core/settings_contracts.py +25 -0
  132. pz_core/docs/README.md +20 -0
  133. pz_core/docs/distribution-README.md +438 -0
  134. pz_core/docs/pz/345/221/275/344/273/244/346/200/273/350/247/210.md +1011 -0
  135. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/00-pz/346/236/266/346/236/204/346/200/273/347/272/262.md +240 -0
  136. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/01-/347/233/256/345/275/225/344/270/216/345/210/206/345/261/202/350/247/204/350/214/203.md +216 -0
  137. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/02-/345/214/205/344/270/216/345/210/206/345/217/221/350/247/204/350/214/203.md +403 -0
  138. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/03-App/346/234/200/345/260/217/345/255/220/345/214/205/350/247/204/350/214/203.md +571 -0
  139. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/04-Settings/344/270/216/351/205/215/347/275/256/350/247/204/350/214/203.md +1177 -0
  140. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/05-/350/265/204/346/272/220/344/270/216/350/277/220/350/241/214/347/233/256/345/275/225/350/247/204/350/214/203.md +575 -0
  141. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/06-/346/225/260/346/215/256/345/272/223/344/270/216/345/217/257/351/200/211/345/237/272/347/241/200/350/256/276/346/226/275/350/247/204/350/214/203.md +499 -0
  142. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/07-App/346/233/277/346/215/242/350/247/204/350/214/203.md +206 -0
  143. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/08-/345/271/263/345/217/260/347/272/247App/344/270/216/350/277/220/350/241/214/346/227/266/345/215/225/344/276/213/350/247/204/350/214/203.md +445 -0
  144. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/09-CLI/344/270/216Worker/345/205/245/345/217/243/350/247/204/350/214/203.md +950 -0
  145. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/10-/346/265/213/350/257/225/344/270/216/345/245/221/347/272/246/350/247/204/350/214/203.md +1001 -0
  146. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/11-/344/273/223/345/272/223/347/234/237/347/233/270/346/272/220/345/234/260/345/233/276.md +102 -0
  147. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/12-/346/236/204/345/273/272/344/270/216/345/217/221/345/270/203/346/243/200/346/237/245/346/270/205/345/215/225.md +171 -0
  148. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/13-/346/255/243/345/274/217Facade/346/200/273/350/241/250.md +402 -0
  149. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/14-/345/210/206/345/261/202/344/270/216/346/211/251/345/261/225/350/247/204/350/214/203.md +550 -0
  150. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/15-/345/255/220/345/214/205/345/274/200/345/217/221/344/275/277/347/224/250/346/211/213/345/206/214.md +1004 -0
  151. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/16-/346/211/251/345/261/225/347/202/271/346/255/243/345/274/217/345/215/217/350/256/256/346/270/205/345/215/225.md +352 -0
  152. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/17-pz.protocols.v1/346/255/243/345/274/217/345/215/217/350/256/256.md +266 -0
  153. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/18-/346/211/251/345/261/225/347/202/271/345/206/262/347/252/201/350/243/201/345/206/263/346/200/273/350/241/250.md +131 -0
  154. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/19-/351/224/231/350/257/257/347/240/201/344/270/216/350/257/212/346/226/255/345/215/217/350/256/256.md +176 -0
  155. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/20-/347/247/201/346/234/211/345/214/205/344/273/223/344/270/216/347/273/237/344/270/200/350/256/244/350/257/201/351/203/250/347/275/262/350/247/204/350/214/203.md +243 -0
  156. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/21-contrib/347/272/246/345/256/232/344/270/216Provider/346/216/245/345/205/245/350/247/204/350/214/203.md +365 -0
  157. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/22-/345/244/232/345/233/275/350/257/255/350/250/200/350/265/204/346/272/220/346/262/273/347/220/206/344/270/216/346/214/211/351/234/200/345/212/240/350/275/275/350/247/204/350/214/203.md +912 -0
  158. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/23-/351/246/226/345/261/217/345/261/225/347/244/272/344/270/216/351/246/226/345/270/247/347/250/263/345/256/232/350/247/204/350/214/203.md +269 -0
  159. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/24-pz/345/220/257/345/212/250/344/270/216/350/277/220/350/241/214/346/227/266/347/274/226/350/257/221/346/265/201/347/250/213.md +245 -0
  160. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/25-/351/235/242/345/220/221/345/257/271/350/261/241/345/274/200/345/217/221/350/247/204/350/214/203.md +300 -0
  161. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/README.md +98 -0
  162. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/00-legacy_core/346/240/207/345/207/206pip/345/214/205/345/214/226/351/207/215/346/236/204/346/200/273/347/272/262.md +1073 -0
  163. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/01-/347/233/256/345/275/225/344/270/216/345/210/206/345/261/202/351/207/215/346/236/204/346/211/247/350/241/214/346/226/271/346/241/210.md +224 -0
  164. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/02-/345/214/205/344/270/216/345/210/206/345/217/221/350/247/204/350/214/203-/345/220/253/345/216/206/345/217/262/345/217/243/345/276/204.md +671 -0
  165. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/13-/344/270/200/346/254/241/346/200/247/345/256/236/346/226/275/346/270/205/345/215/225.md +387 -0
  166. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/14-/345/255/220/345/214/205/351/200/202/351/205/215/346/224/271/351/200/240/346/270/205/345/215/225.md +592 -0
  167. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/15-infra/350/243/201/345/211/252/344/270/216/350/220/275/344/275/215/346/270/205/345/215/225.md +81 -0
  168. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/19-core/345/215/225/344/270/200/346/255/243/345/274/217/346/226/271/346/241/210/346/224/266/346/225/233/346/211/247/350/241/214/346/270/205/345/215/225.md +161 -0
  169. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/25-/345/215/217/350/256/256/345/210/260/346/272/220/347/240/201/345/267/256/350/267/235/346/270/205/345/215/225.md +377 -0
  170. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/26-cutover/345/256/214/345/267/245/346/243/200/346/237/245/346/270/205/345/215/225.md +117 -0
  171. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/29-/345/244/232/345/233/275/350/257/255/350/250/200/350/265/204/346/272/220/346/262/273/347/220/206/344/270/216/346/214/211/351/234/200/345/212/240/350/275/275/350/247/204/350/214/203-/345/220/253/350/277/201/347/247/273/347/255/226/347/225/245.md +956 -0
  172. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/99-/350/265/204/346/272/220/346/224/266/345/217/243/345/220/216/347/273/255TODO.md +84 -0
  173. pz_core/docs//345/217/221/347/211/210/345/211/215/346/270/205/345/215/225-4.0.11.md +84 -0
  174. pz_core/docs//346/226/260/346/211/213/346/214/207/345/215/227.md +422 -0
  175. pz_core/infra/__init__.py +17 -0
  176. pz_core/infra/db/__init__.py +23 -0
  177. pz_core/infra/db/contracts.py +73 -0
  178. pz_core/infra/health/__init__.py +5 -0
  179. pz_core/infra/health/contracts.py +8 -0
  180. pz_core/infra/kv/__init__.py +5 -0
  181. pz_core/infra/kv/contracts.py +17 -0
  182. pz_core/infra/observability/__init__.py +23 -0
  183. pz_core/infra/observability/contracts.py +142 -0
  184. pz_core/infra/resilience/__init__.py +5 -0
  185. pz_core/infra/resilience/contracts.py +17 -0
  186. pz_core/infra/resources/__init__.py +5 -0
  187. pz_core/infra/resources/contracts.py +16 -0
  188. pz_core/infra/settings/__init__.py +17 -0
  189. pz_core/infra/settings/models.py +81 -0
  190. pz_core/infra/storage/__init__.py +5 -0
  191. pz_core/infra/storage/ports.py +27 -0
  192. pz_core/project_root_commands.py +282 -0
  193. pz_core/project_runtime_cli.py +344 -0
  194. pz_core/project_sources.py +253 -0
  195. pz_core/pz_launcher.py +209 -0
  196. pz_core/root_cli.py +70 -0
  197. pz_core/runtime/__init__.py +229 -0
  198. pz_core/runtime/access.py +86 -0
  199. pz_core/runtime/adapters/__init__.py +5 -0
  200. pz_core/runtime/adapters/base.py +18 -0
  201. pz_core/runtime/asyncio_bridge.py +18 -0
  202. pz_core/runtime/bindings/__init__.py +5 -0
  203. pz_core/runtime/bindings/container.py +50 -0
  204. pz_core/runtime/cli/__init__.py +5 -0
  205. pz_core/runtime/cli/_args.py +67 -0
  206. pz_core/runtime/cli/app_commands.py +417 -0
  207. pz_core/runtime/cli/check.py +214 -0
  208. pz_core/runtime/cli/db.py +297 -0
  209. pz_core/runtime/cli/main.py +89 -0
  210. pz_core/runtime/cli/resources.py +280 -0
  211. pz_core/runtime/discovery.py +23 -0
  212. pz_core/runtime/environment_integrity.py +157 -0
  213. pz_core/runtime/errors.py +5 -0
  214. pz_core/runtime/events.py +87 -0
  215. pz_core/runtime/facades/__init__.py +38 -0
  216. pz_core/runtime/facades/_capabilities.py +46 -0
  217. pz_core/runtime/facades/cache.py +103 -0
  218. pz_core/runtime/facades/database.py +71 -0
  219. pz_core/runtime/facades/events.py +380 -0
  220. pz_core/runtime/facades/health.py +101 -0
  221. pz_core/runtime/facades/mail.py +121 -0
  222. pz_core/runtime/facades/observability.py +102 -0
  223. pz_core/runtime/facades/providers.py +75 -0
  224. pz_core/runtime/facades/resources.py +52 -0
  225. pz_core/runtime/facades/storage.py +296 -0
  226. pz_core/runtime/health/__init__.py +5 -0
  227. pz_core/runtime/health/service.py +102 -0
  228. pz_core/runtime/host.py +188 -0
  229. pz_core/runtime/host_runner.py +282 -0
  230. pz_core/runtime/http/__init__.py +20 -0
  231. pz_core/runtime/http/app.py +576 -0
  232. pz_core/runtime/http_timing.py +173 -0
  233. pz_core/runtime/invocation.py +29 -0
  234. pz_core/runtime/json_safe.py +47 -0
  235. pz_core/runtime/lifecycle/__init__.py +25 -0
  236. pz_core/runtime/lifecycle/manager.py +533 -0
  237. pz_core/runtime/plan.py +214 -0
  238. pz_core/runtime/plan_context.py +33 -0
  239. pz_core/runtime/plan_snapshot.py +177 -0
  240. pz_core/runtime/registries/__init__.py +27 -0
  241. pz_core/runtime/registries/capabilities.py +101 -0
  242. pz_core/runtime/registries/providers.py +396 -0
  243. pz_core/runtime/resource_assets.py +344 -0
  244. pz_core/runtime/run_cli.py +211 -0
  245. pz_core/runtime/settings.py +888 -0
  246. pz_core/runtime/worker/__init__.py +21 -0
  247. pz_core/runtime/worker/engine.py +921 -0
  248. pz_core/runtime/worker/runtime.py +25 -0
  249. pz_core/testing/__init__.py +15 -0
  250. pz_core/testing/doc_route_contracts.py +305 -0
  251. pz_core/utils/__init__.py +44 -0
  252. pz_core/utils/env.py +65 -0
  253. pz_core/utils/project_jsonc/__init__.py +29 -0
  254. pz_core/utils/project_jsonc/document.py +420 -0
  255. pz_core/utils/project_settings.py +99 -0
  256. pz_foundation/__init__.py +50 -0
  257. pz_foundation/_contracts.py +81 -0
  258. pz_foundation/_errors.py +34 -0
  259. pz_foundation/api.py +14 -0
  260. pz_foundation/apps.py +127 -0
  261. pz_foundation/auth/__init__.py +1 -0
  262. pz_foundation/cache_control.py +233 -0
  263. pz_foundation/cli/__init__.py +2 -0
  264. pz_foundation/cli/workers.py +565 -0
  265. pz_foundation/configs/__init__.py +0 -0
  266. pz_foundation/configs/default.env +15 -0
  267. pz_foundation/configs/default.jsonc +452 -0
  268. pz_foundation/db/__init__.py +94 -0
  269. pz_foundation/db/aerich_executor.py +664 -0
  270. pz_foundation/db/config.py +358 -0
  271. pz_foundation/db/connection_manager.py +689 -0
  272. pz_foundation/db/connection_probe.py +204 -0
  273. pz_foundation/db/database.py +1291 -0
  274. pz_foundation/db/deps.py +47 -0
  275. pz_foundation/db/lifecycle_status.py +85 -0
  276. pz_foundation/db/migrations.py +477 -0
  277. pz_foundation/db/mysql.py +57 -0
  278. pz_foundation/db/postgresql.py +409 -0
  279. pz_foundation/db/production_pool_fallback.py +143 -0
  280. pz_foundation/db/public.py +45 -0
  281. pz_foundation/db/session.py +369 -0
  282. pz_foundation/db/sqlite.py +15 -0
  283. pz_foundation/db/tortoise_manager.py +724 -0
  284. pz_foundation/db/tortoise_psycopg.py +148 -0
  285. pz_foundation/docs/README.md +18 -0
  286. pz_foundation/docs/distribution-README.md +319 -0
  287. pz_foundation/docs/infra/350/243/201/345/211/252/344/270/216/350/220/275/344/275/215/350/257/264/346/230/216.md +39 -0
  288. pz_foundation/docs//345/205/254/345/274/200/345/205/215/350/264/271/346/216/245/345/217/243/351/230/262/346/273/245/347/224/250/344/270/216/344/272/272/346/234/272/346/240/241/351/252/214.md +680 -0
  289. pz_foundation/docs//350/256/276/350/256/241/346/226/207/346/241/243.md +288 -0
  290. pz_foundation/docs//350/267/257/347/224/261/344/270/216URL/345/244/204/347/220/206/350/247/204/350/214/203.md +119 -0
  291. pz_foundation/health.py +105 -0
  292. pz_foundation/host/__init__.py +11 -0
  293. pz_foundation/host/builder.py +34 -0
  294. pz_foundation/host/cache_runtime.py +44 -0
  295. pz_foundation/host/db/__init__.py +17 -0
  296. pz_foundation/host/db/aerich_config.py +36 -0
  297. pz_foundation/host/db/config.py +106 -0
  298. pz_foundation/host/db/connection_manager.py +48 -0
  299. pz_foundation/host/db/deps.py +5 -0
  300. pz_foundation/host/db/orm_retry_patch.py +566 -0
  301. pz_foundation/host/db/request_adapter.py +325 -0
  302. pz_foundation/host/db/router.py +28 -0
  303. pz_foundation/host/db/session.py +284 -0
  304. pz_foundation/host/db/tortoise_manager.py +10 -0
  305. pz_foundation/host/idempotency.py +132 -0
  306. pz_foundation/host/lifespan.py +247 -0
  307. pz_foundation/host/redis_runtime.py +100 -0
  308. pz_foundation/host/web.py +790 -0
  309. pz_foundation/infra_provider.py +236 -0
  310. pz_foundation/kv/__init__.py +25 -0
  311. pz_foundation/kv/cache.py +1166 -0
  312. pz_foundation/kv/deps.py +84 -0
  313. pz_foundation/kv/public.py +25 -0
  314. pz_foundation/kv/redis_manager.py +1531 -0
  315. pz_foundation/lazy_exports.py +43 -0
  316. pz_foundation/lazy_imports.py +124 -0
  317. pz_foundation/lifecycle.py +392 -0
  318. pz_foundation/managed_workers.py +1117 -0
  319. pz_foundation/middlewares/__init__.py +26 -0
  320. pz_foundation/middlewares/_settings.py +250 -0
  321. pz_foundation/middlewares/analytics.py +124 -0
  322. pz_foundation/middlewares/cache_control.py +156 -0
  323. pz_foundation/middlewares/compression.py +202 -0
  324. pz_foundation/middlewares/cors.py +62 -0
  325. pz_foundation/middlewares/geo_country.py +44 -0
  326. pz_foundation/middlewares/inner_router.py +50 -0
  327. pz_foundation/middlewares/rate_limited/__init__.py +3 -0
  328. pz_foundation/middlewares/rate_limited/key_limiter.py +115 -0
  329. pz_foundation/middlewares/request_logging.py +104 -0
  330. pz_foundation/middlewares/route_owner.py +309 -0
  331. pz_foundation/middlewares/security_csrf.py +44 -0
  332. pz_foundation/middlewares/security_headers/__init__.py +4 -0
  333. pz_foundation/middlewares/security_headers/conflicts.py +44 -0
  334. pz_foundation/middlewares/security_headers/csp_nonce.py +200 -0
  335. pz_foundation/middlewares/security_headers/security_headers.py +44 -0
  336. pz_foundation/migrations/__init__.py +1 -0
  337. pz_foundation/migrations/pz_foundation/__init__.py +1 -0
  338. pz_foundation/models.py +22 -0
  339. pz_foundation/notifications/__init__.py +23 -0
  340. pz_foundation/notifications/email.py +542 -0
  341. pz_foundation/notifications/public.py +31 -0
  342. pz_foundation/observability/__init__.py +71 -0
  343. pz_foundation/observability/http.py +34 -0
  344. pz_foundation/observability/logger/__init__.py +49 -0
  345. pz_foundation/observability/logger/_bot_settings.py +126 -0
  346. pz_foundation/observability/logger/bot_message.py +592 -0
  347. pz_foundation/observability/logger/core.py +385 -0
  348. pz_foundation/observability/logger/delivery_audit.py +84 -0
  349. pz_foundation/observability/logger/feishu_bot.py +199 -0
  350. pz_foundation/observability/logger/telegram_bot.py +194 -0
  351. pz_foundation/observability/metrics.py +365 -0
  352. pz_foundation/observability/runtime.py +68 -0
  353. pz_foundation/public.py +405 -0
  354. pz_foundation/resources/__init__.py +12 -0
  355. pz_foundation/resources/builder.py +900 -0
  356. pz_foundation/resources/images.py +363 -0
  357. pz_foundation/root_cli.py +20 -0
  358. pz_foundation/routing/__init__.py +203 -0
  359. pz_foundation/routing/canonical.py +486 -0
  360. pz_foundation/routing/convertors.py +82 -0
  361. pz_foundation/routing/declarations.py +843 -0
  362. pz_foundation/routing/exports.py +148 -0
  363. pz_foundation/routing/loaders.py +138 -0
  364. pz_foundation/routing/manifest.py +215 -0
  365. pz_foundation/routing/manifest_provider.py +47 -0
  366. pz_foundation/routing/mounts.py +101 -0
  367. pz_foundation/routing/ordering.py +172 -0
  368. pz_foundation/routing/registry.py +133 -0
  369. pz_foundation/routing/utils.py +28 -0
  370. pz_foundation/runtime.py +234 -0
  371. pz_foundation/settings.py +253 -0
  372. pz_foundation/settings_normalization.py +130 -0
  373. pz_foundation/storage/__init__.py +25 -0
  374. pz_foundation/storage/local.py +209 -0
  375. pz_foundation/storage/public.py +106 -0
  376. pz_foundation/storage/s3.py +116 -0
  377. pz_foundation/storage/service.py +234 -0
  378. pz_foundation/urls.py +19 -0
  379. pz_foundation/utils/__init__.py +6 -0
  380. pz_foundation/utils/merge.py +160 -0
  381. pz_foundation/worker_replicas.py +55 -0
  382. pz_settings_center/__init__.py +5 -0
  383. pz_settings_center/api/__init__.py +5 -0
  384. pz_settings_center/api/runtime_overrides.py +360 -0
  385. pz_settings_center/apps.py +28 -0
  386. pz_settings_center/configs/__init__.py +0 -0
  387. pz_settings_center/configs/default.env +2 -0
  388. pz_settings_center/configs/default.jsonc +1 -0
  389. pz_settings_center/docs/01-settings_center/350/203/275/345/212/233/350/276/271/347/225/214.md +124 -0
  390. pz_settings_center/docs/README.md +12 -0
  391. pz_settings_center/docs/distribution-README.md +150 -0
  392. pz_settings_center/docs//350/256/276/350/256/241/346/226/207/346/241/243.md +48 -0
  393. pz_settings_center/integration_payload.py +18 -0
  394. pz_settings_center/project_helpers.py +405 -0
  395. pz_settings_center/public.py +139 -0
  396. pz_settings_center/routing_helpers.py +73 -0
  397. pz_settings_center/runtime_override_provider.py +61 -0
  398. pz_settings_center/typed_settings.py +324 -0
  399. pz_settings_center/urls.py +9 -0
@@ -0,0 +1,1001 @@
1
+ # 测试与契约规范
2
+
3
+ > settings 契约统一口径:运行时代码只读 `settings_center.settings`;app 配置只读 `settings_center.settings.<app_id>`;项目级读取只允许 `settings_center.settings.project.<field>`;`load_app_settings()`、`load_typed_app_settings()`、`load_<app>_settings()` 和任何绕开 `settings_center` 的重复 helper 都不再是正式读取链。
4
+ > namespace 契约统一口径:叶子 app 的 namespace 固定为其最终生效的 `app.id`;标准 `apps.py` 必须通过模块级 `app` 对象显式声明 `app.id`,目录名/import 包名默认应与 `app.id` 收敛一致;非叶子内部模块不得自定义 namespace,只能读取父 app 的 settings namespace,或读取 `settings_center.settings.project.<field>`。
5
+ > 基础层契约统一口径:`pz` 是唯一正式基础包与唯一可独立启动的包。
6
+
7
+ ## 1. 目标
8
+
9
+ 新设计不能只停留在文档层,必须通过契约测试证明:
10
+
11
+ - 安装包模式成立
12
+ - 项目目录边界成立
13
+ - 可选基础设施成立
14
+ - app 替换与平台 app 边界成立
15
+
16
+ ## 2. 测试分层
17
+
18
+ 测试固定分为四层:
19
+
20
+ ### 2.1 单元测试
21
+
22
+ 覆盖:
23
+
24
+ - 路径解析
25
+ - 配置优先级
26
+ - app settings 聚合
27
+ - 资源发现顺序
28
+ - 单例 reset / lazy factory 行为
29
+
30
+ ### 2.2 契约测试
31
+
32
+ 覆盖:
33
+
34
+ - 标准入口
35
+ - 安装包运行方式
36
+ - app 替换
37
+ - 平台 app 公共门面
38
+ - 扩展点冲突策略
39
+ - system checks / `pz check`
40
+
41
+ ### 2.3 集成测试
42
+
43
+ 覆盖:
44
+
45
+ - SQLite
46
+ - MySQL
47
+ - PostgreSQL
48
+ - Redis 可选启用
49
+ - 资源合并
50
+ - 迁移聚合
51
+ - 发布升级与回滚验证
52
+
53
+ ### 2.4 烟雾测试
54
+
55
+ 覆盖:
56
+
57
+ - `pz --help`
58
+ - 最小 app 启动
59
+ - 最小 worker 启动
60
+
61
+ ### 2.5 架构契约测试
62
+
63
+ 覆盖:
64
+
65
+ - 分层依赖方向
66
+ - 唯一真相源不被重复实现
67
+ - facade/capability 边界
68
+ - 编译阶段与执行阶段分离
69
+ - 核心构件命名与职责唯一化
70
+ - `pz.runtime` 与 workspace `devtools/*` 的边界
71
+ - owner-specific host/runtime 的归属边界
72
+
73
+ 验证点:
74
+
75
+ - 上层模块不会直接 import 下层具体实现细节
76
+ - 子包不会自行重写 settings/app/resource/infra 聚合逻辑
77
+ - route/middleware/resource/infra 只存在一套正式 manifest/snapshot 编译链
78
+ - Web / CLI / worker 共用同一条 `RuntimePlan` 编译链
79
+ - 编译阶段不会偷偷触发运行时副作用
80
+ - `Compiler` / `Registry` / `Facade` / `Provider` / `Adapter` 的职责边界可被静态检查或契约测试证明
81
+ - `manager` / `service` / `helper` 不会被实现成稳定公共契约入口
82
+ - 最小子包源码树 / 外部 app / scripts 不会直接 import `pz.infra.*` 具体实现
83
+ - `pz.runtime/*` 与 workspace `devtools/*` 不会点名任何 `apps.<owner>` 包路径
84
+ - owner-specific host/runtime 入口不会留在 `pz`
85
+
86
+ ## 3. 必测场景矩阵
87
+
88
+ ### 3.1 安装包模式
89
+
90
+ 必须测试:
91
+
92
+ - 构建 wheel
93
+ - 安装到全新虚拟环境
94
+ - 从非源码目录执行 `pz`
95
+
96
+ 验证点:
97
+
98
+ - 不依赖仓库根路径
99
+ - 不依赖 vendored 子目录
100
+ - 不向 `site-packages` 写入任何运行态文件
101
+ - 官方最小子包装态安装后可稳定 import 自己声明的正式 Python 包
102
+ - 正式 Python 包的 import 成立不依赖源码 checkout、editable 特例或 `PYTHONPATH` 注入
103
+ - `BASE_URL` / `API_BASE_URL` / `IMAGE_BASE_URL` / `worker_storage_path` 的最终值来自单一归一化阶段,而不是 bootstrap 二次补写
104
+ - 外部项目依赖面只来自 `pz`、`pz.app`、`<app>.public`
105
+ - base 安装下执行 `pz --help`、`pz list`、`pz check` 不会拉起重依赖初始化
106
+
107
+ 补充边界:
108
+
109
+ - 官方最小子包的 import contract 属于对应最小子包装态验证,不等于要求 `pz` base wheel 自带这些模块
110
+ - 若采用分仓布局,官方 app 真相源位于独立 `apps` 工作区根下的 `apps/`
111
+ - 因此 `pz` 基础包只负责 base wheel/base-only 验证;官方维护最小子包 import smoke 应在对应 app 组合包安装后单独验证
112
+
113
+ ### 3.1.1 system checks 契约
114
+
115
+ 必须测试:
116
+
117
+ - `pz check` 可在 wheel 安装模式下运行
118
+ - `pz check --json` 输出稳定结构
119
+ - Web/debug/preflight 若展示检查结果,内容与 `pz check` 同源
120
+
121
+ 验证点:
122
+
123
+ - system checks 只有一条正式入口链
124
+ - `CheckReport` 结构稳定且可审计
125
+ - 不存在第二套 best-effort 检查主链
126
+ - 新增检查必须先覆盖所属领域的无副作用原子诊断能力,再覆盖 `pz check` 的编排接入;不得只测试 CLI 输出而跳过领域原子能力
127
+ - `CheckReport` 中的 `severity`、`code`、`owner`、`scope` 语义稳定
128
+
129
+ ### 3.2 项目目录与配置
130
+
131
+ 必须测试:
132
+
133
+ - `pz.jsonc`
134
+ - `PZ_PROJECT_DIR`
135
+ - `--project-dir`
136
+
137
+ 验证点:
138
+
139
+ - 配置优先级一致
140
+ - worker 与 web 解析结果一致
141
+ - 相对路径统一相对 `project_dir`
142
+ - `.env` 只在 `project_dir` 作用域内生效,不再混入框架仓库 `ROOT_DIR/.env`
143
+ - 配置加载不会把合并后的结果写回 `os.environ`
144
+ - environment/profile 候选名解析规则稳定且可预测,不依赖 loader 内部隐式扩展
145
+ - 导入 `pz.settings` 本身不会提前锁定某个 `project_dir` / env snapshot 并污染后续入口
146
+ - 导入 `pz_settings_center.public` 本身不会提前编译或绑定 `SettingsSnapshot`
147
+ - `settings_center.settings` 首次访问会按 settings lazy autoload 规则解析当前项目并读取最终 settings
148
+ - 直接脚本场景可通过 `PZ_PROJECT_DIR` 在非项目 cwd 下读取 `settings_center.settings.<app_id>`
149
+ - settings lazy autoload 不会编译完整 `RuntimePlan`,也不会触发 route / middleware / resource / infra / worker lifecycle 装配
150
+ - settings provider 在 lazy autoload 下保持轻量、确定、无网络/DB/Redis/资源 build 副作用
151
+ - app settings 聚合结果同时反映:
152
+ - 包内默认值
153
+ - `[apps.<app_id>]`
154
+ - 环境变量
155
+ - app settings provider
156
+ - runtime override
157
+ - 空 `installed_apps` 不会再被 helper 自动回退到一长串类默认清单
158
+ - app settings 的 `get_settings()` 等历史读取入口不再允许作为默认值主链;遗留入口必须直接报错,运行时代码统一只读 `settings_center.settings`
159
+ - settings runtime owner 读取“文件默认值”时不会重新跑一遍全量 app 装配主链
160
+ - 配置装配阶段不会触发网络 I/O、DB/Redis 初始化或资源 build
161
+ - settings snapshot 在编译完成后保持只读,不会在 bootstrap/lifespan/startup 中被原地改写
162
+ - `AppSettings` / `CoreSettings` validator 不承担跨 app 裁决、provider 注册或运行时副作用
163
+ - 已删除的历史环境选择字段与旧键写法会稳定报错,不再静默忽略或自动迁移
164
+ - 运行时代码不会再通过 `pz.settings`、`os.environ`、重读 `pz.jsonc` 形成第二套配置读取路径
165
+ - 所有派生字段都只来自单一 settings compiler,而不会在 bootstrap/middleware/worker 中再次重算
166
+ - `settings_center.settings.<app_id>` 与 runtime settings proxy 的 app 读取顺序一致,都是:
167
+ - snapshot
168
+ - provider
169
+ - runtime override
170
+ - provider 补值不会反向改写 `SettingsSnapshot`
171
+ - 启动前必须可直接从 snapshot 读到 DB/Redis 等基础连接配置,不依赖 provider 兜底
172
+ - 如果声明了 settings provider,其 patch 必须在 `RuntimePlan.settings` 编译阶段生效;app lifecycle startup 不得靠具体 owner 插队来完成 settings 裁决
173
+ - runtime override schema 只从 `AppSettings.__runtime_overrides__` 稳定提取
174
+ - `__runtime_overrides__` 配置错误时,注册期必须直接失败,不能静默跳过
175
+ - 未注册 namespace / 未声明 key / 类型不匹配的 runtime override 写入必须被拒绝
176
+ - 自定义 `app.id` 不会破坏 settings namespace 绑定;目录名与 `app.id` 不一致时,运行时仍只认显式 `app.id`
177
+ - 非叶子内部模块不会因目录名或模块路径自动获得新的 settings namespace
178
+ - `live / refresh / restart_required` 必须在运行时写入行为上可观测地区分
179
+
180
+ ### 3.3 运行目录
181
+
182
+ 必须测试:
183
+
184
+ - `cache_dir`
185
+ - `runtime_dir`
186
+ - `asset_dir`
187
+
188
+ 验证点:
189
+
190
+ - 资源合并输出进入 `cache_dir`
191
+ - 锁与 pid 进入 `runtime_dir`
192
+ - 上传、本地存储、长期身份、账号、记忆、浏览器环境 inventory 等不可删除文件进入 `asset_dir`
193
+ - 本地 storage 默认值不再回落到相对 `"uploads"` / `"var/media"`
194
+ - 相对资源目录 helper 不再回落到 `Path.cwd()`
195
+ - 资源 merge 失败时不会污染当前已激活产物目录
196
+ - 公开挂载不会把整个 `runtime_dir` / `asset_dir` 暴露出去
197
+ - 资源诊断页、CLI 诊断、debug surface 都读取同一份资源 manifest,而不是各自重扫目录
198
+ - 资源服务不会在 import `core/resources/*` 时基于全局 settings/path 快照提前实例化
199
+ - Web `startup()` / `create_app()` / `create_host_app()` 不会默认触发 asset build、图片生成、bundle/minify
200
+ - `file_service` / `StorageFacade.url()` 在无公共访问语义时稳定返回 `None` 或显式能力错误,而不是回退为宿主机绝对路径、相对路径或 `file://`
201
+ - `file_service presign` / `resolve_url` 不会把“缺失 public base_url / backend 未声明”伪装成可公开访问的 URL 成功
202
+
203
+ ### 3.3.1 路由增强与冲突检测
204
+
205
+ 必须测试:
206
+
207
+ - `base_prefix` 展开正确
208
+ - `api_version=true` 时路径前缀展开正确
209
+ - `i18n_aware=true` 时按正式语言列表显式展开默认语言路径与 `/{lang}` 路径
210
+ - 路由冲突在真实注册前即可被检测
211
+ - core health/metrics/debug routes 与 app routes 的注册顺序可预测,且不靠后续重排兜底
212
+
213
+ 验证点:
214
+
215
+ - route enhancer 的展开规则是稳定契约
216
+ - 不再依赖“先挂载、再清理重排、最后报错”的流程
217
+ - `"/"` / `"/{path:path}"` / `"/{lang}/{module}"` 这类 generic route 的分类与排序规则形成稳定契约,或被显式替代
218
+ - `i18n_aware` 统一按配置语言列表显式展开,不再保留“只生成通用 `/{lang}` 参数路由”的两套展开语义
219
+
220
+ ### 3.4 可选基础设施
221
+
222
+ 必须测试:
223
+
224
+ - 无 DB 启动
225
+ - 无 Redis 启动
226
+ - SQLite 启动与迁移
227
+ - MySQL 启动与迁移
228
+ - PostgreSQL 启动与迁移
229
+
230
+ 验证点:
231
+
232
+ - 未启用组件不参与 readiness
233
+ - 未启用组件不在 startup 阶段初始化
234
+ - 缺失驱动时错误信息明确
235
+ - `InfraSettings` / `InfraContext` 在 disabled 场景下能正确表达 `disabled` 而不是伪造默认 host/port
236
+ - `/ready` 不再无条件初始化 DB/Redis
237
+ - Redis 未启用时不会回退到 `127.0.0.1:6379`
238
+ - disabled 组件不会把整体 readiness 错误降级为 `degraded`
239
+ - metrics/tracing 关闭时不会影响主业务启动链
240
+ - metrics 默认标签不会引入高基数敏感值
241
+ - base 安装命令路径不会因缺失可选驱动而在无关命令上失败
242
+ - `create_app()`、Web `startup()`、`setup_infra()`、`/ready` 都不会触发 `generate_schemas`、自动建表、迁移修补或 schema auto-heal
243
+ - DB 初始化、连通性校验、schema/migration 执行是三段可区分行为;启动失败诊断不会把 DDL 副作用混进 readiness / bootstrap
244
+ - `.sources` 官方/共享包只走数据库 init,不走 Aerich 历史迁移链;契约测试必须证明共享包不会出现在 migration executor 的 runnable owners 中
245
+ - 业务宿主包仍可作为 migration executor owner 执行自己的迁移链
246
+
247
+ ### 3.5 app 替换与平台 app
248
+
249
+ 必须测试:
250
+
251
+ - 外部 app 显式替换官方维护最小子包
252
+ - `[app_replacements]` 是唯一正式替换声明入口
253
+ - 重复 `app_id` 默认失败
254
+ - `requires_apps` 缺失依赖时报错
255
+ - app dependency cycle 报错
256
+ - app import path 解析不会依赖 `pz.<name>` 自动补前缀或 package-level `app` 猜测才成立
257
+ - 最小安装场景不会自动装载整套默认官方 app / 独立子包清单
258
+ - shared site UI owner 被替换后的模板公共门面保持同一公开契约
259
+ - settings runtime owner 缺失时错误明确
260
+ - task dispatch / worker owner 未启用 Redis 时行为明确
261
+ - provider registry 在平台 app 替换后不会残留旧 provider
262
+ - shared site UI owner 模板环境在替换后不会复用旧的 globals/merged 目录绑定
263
+ - account / profile owner 这类扩展消费侧在替换后不会同时残留 legacy provider 与 unified registry 两份状态
264
+ - auth owner / task dispatch owner 替换后不会残留旧 provider binding、旧事件订阅或旧后台任务
265
+ - 涉及 DB/存储的 app 替换,不会出现 migration owner / storage owner 双写
266
+ - settings runtime owner 在显式 `project_dir` 与默认项目目录两种场景下都继续委托 core runtime settings 主链
267
+ - settings runtime owner 线上热更新只覆盖显式声明允许热更新的字段,不会把整个 `AppSettings` 默认暴露
268
+
269
+ ### 3.5.1 app registry 与依赖图契约
270
+
271
+ 必须测试:
272
+
273
+ - `installed_apps` 顺序可稳定形成 app registry
274
+ - `installed_apps` 条目语法固定为目录名,不接受文件系统路径、distribution 名或 `<pkg>.apps:app`
275
+ - `installed_apps` 声明文件夹/容器目录时,会稳定展开到嵌套最小子包
276
+ - 最小子包判定只认标准 `apps.py`:
277
+ - 候选目录或其 Python 包目录内存在 `apps.py`
278
+ - `apps.py` 内有 app class
279
+ - app class 内用静态字符串声明 `id`
280
+ - `apps.py` 内有模块级 `app = <AppClass>()`
281
+ - `app` 对象最终可解析出合法 `app.id`
282
+ - 命中最小子包后会停止继续向下扫描其子目录
283
+ - 非 app 模块不允许使用 `apps.py` 作为普通实现文件名,避免被发现链误识别
284
+ - 容器目录内部展开顺序固定为目录名字典序深度优先
285
+ - Web / CLI / worker / resources / settings 消费的是同一份最小子包展开结果,而不是各自再做第二套扫描
286
+ - 依赖拓扑排序结果稳定
287
+ - 官方维护最小子包 缺失 `required_pz` 时失败
288
+ - `required_pz` 不满足时失败
289
+ - 重复 `app_id`、错误 import path、suffix match 命中错误 app 时都有明确诊断
290
+ - app `init()` 失败时不会被 warning 后静默带入后续装配链
291
+ - `INSTALLED_APPS` 的顺序语义稳定,不会因 env 解析被抹平
292
+ - 旧的短名自动补全、`pz.<name>` 自动补前缀、package-level `app` 猜测不再作为正式成功路径
293
+ - 非源码目录如 `.git/`、`.venv/`、`__pycache__/`、`dist/`、`build/` 不会被发现算法误识别为 app 扫描目标
294
+ - 重复 `app_id` 不再被 debug log 后静默跳过
295
+ - app 元数据不会自触发替换关系,只有 `[app_replacements]` 生效
296
+ - `[app_replacements]` 构成一对一、无环的替换图,不存在链式替换或一对多替换
297
+ - app-level 替换后,被替换 app 不会再残留部分 provider/route/resource 声明进入正式主链
298
+ - app 声明只来自标准 `apps.py` 元数据与正式声明方法,不存在第二套 decorator/registry 声明主链
299
+ - `required_protocols` 只解析 capability contract 名称,不解析具体实现模块路径
300
+
301
+ 验证点:
302
+
303
+ - 保留 Django 风格 app registry / dependency graph 的优点
304
+ - 移除重复 `app_id` 静默跳过这类历史隐式行为
305
+
306
+ ### 3.6 host 边界契约
307
+
308
+ 必须测试:
309
+
310
+ - `pz.runtime` 只暴露正式通用 runtime helper;开发态辅助脚本不进入 installable package,只允许位于仓根 `devtools/*`
311
+ - `pz` 源码树不点名任何 `apps.<owner>` 路径
312
+ - 官方 infra owner 的 DB / Redis / cache / lifespan host 入口位于该 owner 子包内部,不回写 `pz` core
313
+ - task dispatch / worker owner 的 owner-specific host/runtime 入口位于该 owner 子包内部
314
+
315
+ 验证点:
316
+
317
+ - core 不再通过硬编码 import path 支持某个特定 app
318
+ - owner-specific host/runtime 逻辑回到 owner 自己的 app 包
319
+ - 宿主项目不会重新引入 `project_runtime` 一类第二装配层
320
+ - `pz.runtime` 与 workspace `devtools/*` 的存在不等于 `pz` 拥有外部 app 的运行时真相
321
+
322
+ ### 3.6.1 扩展点对象协议契约
323
+
324
+ 必须测试:
325
+
326
+ - `extensions()` 返回值只包含结构化扩展对象
327
+ - 每个扩展对象都带:
328
+ - `kind`
329
+ - `id`
330
+ - `owner_app`
331
+ - `api_version`
332
+ - `priority`
333
+ - 不允许裸函数、字符串路径、import-time 隐式注册器进入正式扩展主链
334
+ - `host.builder` 只能通过 `build(ctx) -> result` 产出 host
335
+ - `settings.provider` 只能通过 `provide(ctx) -> tuple[SettingsPatch, ...]` 返回 patch
336
+ - `cli.tool` 只能通过 `configure(parser)` 与 `run(ctx)` 暴露命令
337
+ - `route.contribution` 只能声明正式 router 来源,不承担最终跨 app 排序裁决
338
+ - `middleware.contribution` 只能声明 factory/replace,不接受历史类路径与隐式补参
339
+ - `lifecycle.contribution` 只能声明 `init/startup/shutdown` 三类正式 phase
340
+ - `resource.contribution` 只能声明标准资源目录来源
341
+ - 同类扩展对象同 `id` 冲突时,按 `priority` 裁决;同优先级冲突直接报错
342
+
343
+ 验证点:
344
+
345
+ - 扩展点声明与实现解耦
346
+ - 冲突治理在编译期完成
347
+ - 不存在第二套隐式扩展注册主链
348
+
349
+ ### 3.6.2 module config / settings / operator 协作链契约
350
+
351
+ 必须测试:
352
+
353
+ - module config owner 只输出结构化 `ModuleConfigPatch`
354
+ - settings surface owner 只消费 patch 并产出最终读取视图
355
+ - settings runtime owner 只在正式声明允许的字段上做 runtime override
356
+ - 运行时代码不会通过 `module_config` 或 settings runtime owner 直接读取业务配置值
357
+ - `ModuleConfigField.type` 只允许正式封闭类型集合
358
+ - `ModuleConfigBinding.target_path` 只允许声明式点路径
359
+ - `transform` 只允许命名、纯函数、可审计的受控转换器
360
+
361
+ 验证点:
362
+
363
+ - 配置声明层、编排层、写入层、读取层职责清晰
364
+ - 不存在第二套运行时配置读取入口
365
+ - module config 不会退化成可执行代码协议
366
+
367
+ ### 3.6.3 `RuntimePlan` 组合根契约
368
+
369
+ 必须测试:
370
+
371
+ - `RuntimePlan` 至少包含:
372
+ - `project`
373
+ - `settings`
374
+ - `app_registry`
375
+ - `route_manifest`
376
+ - `middleware_manifest`
377
+ - `resource_manifest`
378
+ - `cli_manifest`
379
+ - `lifecycle_manifest`
380
+ - `capability_registry`
381
+ - `extension_conflicts`
382
+ - 所有扩展点都先编译进入 `RuntimePlan`
383
+ - app CLI 的 `<app>/cli/<tool>.py` 声明必须先编译进入 `RuntimePlan.cli_manifest`
384
+ - Web / CLI / worker 执行阶段只消费 `RuntimePlan`
385
+ - `RuntimePlan` 生成后保持不可变快照语义
386
+ - `startup()` / `lifespan` / adapter 不会在 `RuntimePlan` 外重新拼装第二份运行图
387
+
388
+ 验证点:
389
+
390
+ - `RuntimePlan` 是唯一运行图组合根
391
+ - 不存在与 `RuntimePlan` 等价的第二套稳定核心名词并行存在
392
+ - 执行阶段不反向修改 manifest、snapshot、registry 或运行图本身
393
+
394
+ ### 3.5.2 架构分层与依赖倒置契约
395
+
396
+ 必须测试:
397
+
398
+ - app 只依赖 public facade / capability contract
399
+ - `pz.settings` 不再作为稳定公共入口被正式主链依赖
400
+ - entrypoint 不直接 import 底层具体 adapter 去拼规则
401
+ - facade 不反向依赖 CLI/Web/worker 入口
402
+
403
+ 验证点:
404
+
405
+ - 依赖方向固定为“入口 -> facade/capability -> compiler/registry -> contract”
406
+ - 不存在反向跨层 import 把内部实现抬成事实公共 API
407
+ - 平台 app 替换后,上层依赖仍通过同一 facade/capability 工作
408
+ - adapter 不会重新读取 settings、重做 app/resource/infra 裁决或维护第二套真相源
409
+ - provider 不会绕过 `RuntimePlan` 直接把默认 client/registry 写进进程级全局状态
410
+ - app 不会把 `CapabilityRegistry` 本身当作稳定公共依赖面
411
+ - `ProjectContext` 是目录边界唯一真相源,不存在第二套 path helper 主链
412
+ - app CLI 命令发现只来自标准 `cli/` 目录,不存在第二套动态注册主链
413
+ - runtime Python 模块不会再从扁平 host proxy 读取 `settings.APP_NAME`、`settings.BASE_URL`、`settings.SITE_URL`、`settings.default_language`、`settings.supported_languages`、`settings.project_dir`
414
+ - 如需读取项目级信息,只允许:
415
+ - `RuntimePlan.settings` 的正式 section
416
+ - `settings_center.public` 的显式 `get_project_*` helper
417
+ - app 自己 namespaced settings facade
418
+ - 非容器级跨子仓 runtime import 默认只允许:
419
+ - 对方 `public.py`
420
+ - 对方 `api/*`
421
+ - 文档与契约测试里冻结的极少量 allowlist 例外
422
+ - allowlist 的治理方向必须是收缩,不允许把旧模块路径长期保留成历史白名单
423
+
424
+ ### 3.5.3 核心构件唯一命名契约
425
+
426
+ 必须测试:
427
+
428
+ - 编译阶段正式对象统一使用 `Compiler` 语义
429
+ - context-bound 索引统一使用 `Registry` 语义
430
+ - app 对外公共入口统一使用 `Facade` 语义
431
+ - 外部系统绑定统一使用 `Provider` / `Adapter` 语义
432
+
433
+ 验证点:
434
+
435
+ - 同一能力不会同时并存 `service`、`manager`、`helper` 三套等价公共入口
436
+ - 文档中的核心对象名称与代码实现名称对齐
437
+ - 不存在与 `RuntimePlan` 等价的第二套稳定核心名词并行存在
438
+
439
+ ### 3.5.4 启动失败与降级语义契约
440
+
441
+ 必须测试:
442
+
443
+ - 必需配置缺失时 fail-fast
444
+ - app `init()` / `startup()` 中的必需依赖错误不会被 warning 后吞掉
445
+ - optional capability 在 disabled / unavailable 时有稳定降级语义
446
+ - 不同装配阶段的失败信息能明确区分:
447
+ - 配置错误
448
+ - app registry 错误
449
+ - route/extension/middleware 冲突
450
+ - provider 缺失
451
+
452
+ 验证点:
453
+
454
+ - 框架默认语义是“显式失败或显式降级”
455
+ - 不再输出 warning 后继续携带半初始化状态运行
456
+ - 不存在 process-wide 默认事件总线、默认 registry、默认 facade 这类正式运行态单例
457
+ - app `init()` 失败不会再被 warning 后继续
458
+ - `startup()` 失败不会再默认带着半初始化 app 进入后续主链
459
+
460
+ ### 3.5.5 生命周期状态机契约
461
+
462
+ 必须测试:
463
+
464
+ - app 生命周期统一遵循 declared -> compiled -> bound -> started -> stopped
465
+ - `init()` 不会启动运行期副作用
466
+ - `startup()` 不会补声明或重编译
467
+ - `shutdown()` 会对称清理 startup 期激活的资源
468
+
469
+ 验证点:
470
+
471
+ - Web / CLI / worker / tests 共享同一套生命周期语义
472
+ - 重复调用 `startup()` / `shutdown()` 不会制造第二套隐式状态迁移
473
+
474
+ ### 3.5.6 错误编码契约
475
+
476
+ 必须测试:
477
+
478
+ - CLI `--json` 失败输出带稳定 `code`
479
+ - `CheckReport` 中的检查项带稳定 `code`
480
+ - 配置错误、装配错误、能力错误、运行错误分别落入正式编码前缀
481
+ - HTTP error envelope 带稳定 `code` 与 `request_id`
482
+ - validation / HTTPException / 500 error 的公共 message 与内部 detail 分层明确
483
+
484
+ 验证点:
485
+
486
+ - 不依赖自由文本断言核心失败语义
487
+ - 不同入口不会为同一类错误发明不同 code
488
+ - 启动失败输出稳定 `stage` / `owner` / `action`
489
+ - 不依赖 `print()`、warning 文本、traceback 偶然内容作为正式故障协议
490
+ - API 错误响应不会直接回显底层异常字符串、密码、token、DSN、宿主机绝对路径等敏感信息
491
+ - request 级错误日志可用 `request_id` 或等价 correlation id 与客户端错误响应稳定关联
492
+
493
+ ### 3.5.7 单一真相源与禁止重复实现契约
494
+
495
+ 必须测试:
496
+
497
+ - `project_dir`、`pz.jsonc`、`installed_apps`、`[app_replacements]` 各自只有一套正式读取入口
498
+ - `<cache_dir>/resources/manifest.json` 是资源结果唯一真相源
499
+ - `InfraSettings` / `InfraContext` 是基础设施状态唯一真相源
500
+ - `RuntimePlan` 是运行图唯一组合根
501
+ - app、worker、CLI 不会各自再实现一套 app 扫描、resource merge、infra 探测
502
+
503
+ 验证点:
504
+
505
+ - 相同问题只存在一个正式编译器/聚合器
506
+ - 其它层只消费结果,不再派生第二份规则
507
+ - `RuntimePlan` 生成后保持不可变快照语义,不会在后续阶段被原地改写
508
+ - `lifespan/startup/shutdown` 不会在 `RuntimePlan` 之外重新拼装第二份运行图
509
+
510
+ ### 3.5.8 编译阶段与执行阶段契约
511
+
512
+ 必须测试:
513
+
514
+ - settings/app/resource/infra 编译阶段不初始化 DB/Redis/metrics/exporter
515
+ - route/middleware 冲突在执行阶段前已完成裁决
516
+ - Web/CLI/worker 执行阶段只消费 `RuntimePlan`,不再重新生成局部计划
517
+
518
+ 验证点:
519
+
520
+ - 编译阶段产物是不可变快照
521
+ - 执行阶段不反向修改 settings/app registry/resource manifest/infra snapshot
522
+ - `startup()` / `lifespan` / adapter 层不会成为第二套规则编译器
523
+ - route 去重、route 重排、legacy extension adapter 不会在执行阶段再补一轮启发式裁决
524
+
525
+ ### 3.5.9 历史 route / extension 启发式清退契约
526
+
527
+ 必须测试:
528
+
529
+ - `admin_panels`、`account_widgets` 这类 legacy app methods 不再是正式扩展声明主链
530
+ - extension 冲突不会再仅靠历史来源优先级的隐式决胜
531
+ - route 冲突不会再通过“先注册、再去重、再重排”兜底
532
+ - `"/"`、`"/{path:path}"`、`"/{lang}/{module}"` 这类 generic route 的处理只依赖正式 manifest 规则
533
+
534
+ 验证点:
535
+
536
+ - legacy extension adapter 不进入正式主链
537
+ - `_CONFLICTS` 一类运行期候选缓存不是正式冲突治理真相源
538
+ - app-local `clean_and_reorder_routes()` 一类后处理 helper 不再控制正式路由主链
539
+
540
+ ### 3.5.10 ORM model discovery 契约
541
+
542
+ 必须测试:
543
+
544
+ - `installed_apps` 驱动 ORM models 自动发现
545
+ - `<app>.models`
546
+ - `<app>.module.models`
547
+ - `<app>.runtime.models`
548
+ - 多级 `models/` 包递归扫描
549
+ - `Meta.app` 与 `app.id` 不一致时报错
550
+
551
+ 验证点:
552
+
553
+ - 保留 Django 风格 “installed apps -> model discovery” 哲学
554
+ - 但发现规则、label 规则、错误诊断都形成稳定契约
555
+ - 有 models 的 app 缺失 `migrations/<app_id>/` 时错误明确
556
+
557
+ ### 3.6 单例与热启动
558
+
559
+ 必须测试:
560
+
561
+ - import-time 单例可 reset
562
+ - 多次创建 app 不重复污染状态
563
+ - 多 worker / 多进程场景锁目录正确
564
+ - 默认事件总线、默认 Redis/client、默认 email/i18n factory 不再作为正式运行模型
565
+ - 包级 `__getattr__` lazy export、模块级 `_DEFAULT_*`、`set_default_*` 不再构成正式公共 API
566
+ - 热重载后二次启动不复用脏状态
567
+ - 全局 provider 注册表可清理
568
+ - 资源服务 / 模块配置服务可按新 settings 重建
569
+ - i18n runtime 默认值与模板环境可按新 project context 重建
570
+ - wildcard event subscriber 与模块级 buffer 可清理
571
+ - 同一进程内切换不同 `project_dir` 时,旧全局 settings 单例 / CLI manager 不会复用前一个项目的全局快照
572
+ - `ensure_full_settings()` 失败时不会吞异常后留下半初始化状态作为正式运行结果
573
+ - `create_app()` / `create_host_app()` 不会隐式触发 assets build/deploy
574
+ - `create_app()` / `create_host_app()` 不会通过全局 setter 重配默认 event bus / Redis config / email config / i18n runtime
575
+ - merged locales 不会在 bootstrap 阶段反向改写全局 i18n runtime 与 modules provider
576
+ - translation manager / module config runtime 不会在不同 `project_dir`、多次 create_app、并行测试中复用脏快照
577
+ - `redirect_rules_cache` / `email_manager` 在不同 project_dir、多次 create_app、测试并行时不会共享脏状态
578
+ - `ResourcesMixin` 初始资源路径计算不再依赖 repo-root helper 或反向 import 包目录
579
+ - `I18nPrefixMiddleware` / `SettingsL2RefreshMiddleware` / redirect middlewares 在不同 project context 下不会复用前一次全局 settings/provider 状态
580
+ - settings snapshot 在入口期完成后,不会再被 bootstrap/lifespan 隐式重写稳定字段
581
+ - `preflight` 优化任务不会在 Web 正式 startup 路径里默认执行
582
+
583
+ ### 3.6.1 扩展点与 middleware 冲突契约
584
+
585
+ 必须测试:
586
+
587
+ - 同 `Extension.id` 默认失败
588
+ - 不再按历史 source rank 静默保留
589
+ - legacy adapter 关闭后,旧式 `admin_*` / `account_*` 声明有明确迁移诊断
590
+ - legacy adapter 若开启,其冲突行为也必须可预测,不得仍走来源优先级静默裁剪
591
+ - middleware 重复声明默认失败
592
+ - middleware 显式 replace 时结果可预测
593
+ - middleware 冲突诊断包含 `id` / `owner`
594
+ - `_CONFLICTS` 不再只是诊断副本,默认失败策略下必须能阻止裁剪后的结果进入正式运行态
595
+
596
+ 验证点:
597
+
598
+ - extension registry 是唯一正式冲突裁决点
599
+ - middleware registry 不再退回“类路径 key + 后写覆盖”
600
+
601
+ ### 3.6.2 Redis / cache / overrides 一致性契约
602
+
603
+ 必须测试:
604
+
605
+ - `redis.enabled = false` 时:
606
+ - 不创建默认 Redis client
607
+ - `/ready` 不探测 Redis
608
+ - cache/L2/version helper 有明确 disabled 语义
609
+ - settings runtime owner 无 Redis 时,多实例一致性策略明确
610
+ - `cache_service` / `redis_manager` / `module_config_service` 可 reset 或按 context 重建
611
+
612
+ 验证点:
613
+
614
+ - 不再依赖默认 `127.0.0.1:6379`
615
+ - 不再依赖“吞异常 + 周期 reload”作为正式跨实例同步模型
616
+ - Redis disabled / unavailable / cache miss / local-only hit 是可区分语义
617
+ - `set_default_redis_config()` / `create_redis()` / `reset_all_redis_connections()` 不会让不同 project context 在同进程内互相污染
618
+ - settings overrides loader / L2 client 不再通过全局 setter 把项目上下文写进进程级单例
619
+ - `configure_settings_db_overrides()` 的 deferred loader 语义不会在 provider 缺失时静默吞掉配置边界错误
620
+ - settings runtime override 写入失败时不会只修改当前进程内存态并伪装成持久化成功
621
+ - module_config_service 不会在 import 时绑定 merged locales 路径与全局 `PZ_ENV`
622
+ - `ModuleConfigL2RefreshMiddleware` 不再依赖“version=0 也周期 reload”来维持正式一致性语义
623
+ - storage 本地回退路径不再隐式合成 `default/media` backend 并写入相对本地目录
624
+ - `StorageServiceMixin` / local storage 默认值会先被统一归一化到 `asset_dir` 语义,而不是直接沿用 `"uploads"` / `"/uploads"`
625
+ - local storage 在无 public base_url 时,不再把 `file://<absolute-path>` 当成公共 URL 语义
626
+ - `module_config_service` / `module_config_manager` 的 `_composed_cache` 不会跨 `project_dir`、多次 `create_app()` 或测试用例复用旧快照
627
+ - `redirect_rules_cache` 的规则快照、版本号与 reload 时间不会跨项目上下文共享;reload / invalidate / clear 操作带有明确 owner 与 project scope
628
+ - settings runtime owner / redirects owner 在 reload 失败或 L2 不可用时不会偷偷沿用失败前的内存快照并伪装成“已完成同步”
629
+
630
+ ### 3.6.3 Middleware 装配契约
631
+
632
+ 必须测试:
633
+
634
+ - middleware kwargs 不再依赖 `__init__` 参数名反射自动猜测
635
+ - `MiddlewareDecl.id` / `owner` 真正参与冲突治理
636
+ - path 声明式 middleware 与 app 声明式 middleware 的冲突默认失败
637
+ - middleware 初始化不会偷偷回退到全局 `settings` / `translation_manager` / ports getter 作为正式配置源
638
+ - redirect middlewares 不在构造期直接缓存来自全局 settings 的规则或 provider 结果
639
+ - middleware 默认规则、默认放行清单、默认保护清单都可被显式审计
640
+ - `MiddlewareDecl.factory` / `ParamsModel` 若声明存在,其行为是正式生效且可测试的;否则不出现在稳定契约中
641
+ - middleware 去重不再按类路径 key 做“最后写入 wins”
642
+ - manager 内部不再依赖 suffix match / normalized match 自动猜 settings 属性
643
+ - request logging / geo_country / audit 中间件不会无条件信任 `X-Forwarded-For`、`X-Real-IP`、`Forwarded`;trusted proxy 链必须显式配置
644
+ - `CORS allow_origins = "*"` 与 credentials 的关系在编译期被显式裁决,不再依赖运行时静默改写
645
+ - CSRF cookie/header/path/mode 之外,cookie 的 `SameSite` / `Secure` / domain / path 语义也必须显式建模并可审计
646
+ - security headers / CSRF 中间件不再在构造期直接 import 全局 `settings`,而是消费编译后的配置快照
647
+ - auth cookie 的 domain / `SameSite` / `Secure` / `HttpOnly` 不再在 middleware/router 运行时临时推导,而是来自显式编译配置
648
+ - `session_recovery` 不会对任意 API 请求拿 refresh token 静默续签;其触发路径、允许方法和失败语义可审计
649
+ - request logging 对 `Authorization`、`Cookie`、`Set-Cookie`、session proof、API key 等敏感头的脱敏不受 allowlist/环境差异影响
650
+
651
+ 验证点:
652
+
653
+ - middleware 生效参数来自显式声明或正式配置映射
654
+ - 不再是“类路径 key + 最后写入 wins”
655
+ - `RedirectMiddlewareMixin` 的默认规则、`SecurityCSRFMixin` / `DenyAPIMixin` / `CmsAuthGuardMixin` 的默认清单,不会在最小安装场景下被偷偷当成基础包强约束
656
+ - manager 内部不再夹带类似“CORS wildcard 自动把 credentials 改成 false”这类未文档化硬编码规则
657
+ - 请求日志、geo_country、风控链路使用的 client IP 解析结果可审计,且不会因为未受信代理头而被伪造
658
+ - Host / Origin / CORS 白名单规则显式且可预测,不依赖 `Host` / `Origin` 头的宽松透传
659
+ - 生产 HTTPS 场景下,安全相关 cookie 不会继续默认以缺失 `Secure`/宽松 `SameSite` 的方式下发
660
+ - `X-User-ID`、认证派生标识等响应头不会在公共边界默认暴露;若启用内部透传,也具备显式 owner 与 deployment scope
661
+ - 请求日志在错误场景、调试场景、JSON 模式下都不会泄露 access token、refresh token、cookie 原文或 session proof
662
+
663
+ ### 3.6.3.1 Runtime Architecture Allowlist 契约
664
+
665
+ 必须测试:
666
+
667
+ - runtime architecture contract 对所有 owner repo 扫描 Python 源码与模板源码
668
+ - 非容器级跨 app import 视图与 allowlist 保持一致
669
+ - allowlist 只记录当前仍存在且仍被允许的正式 import 面
670
+ - 历史文件路径重构后,contract test 必须同步到新的正式真相源
671
+
672
+ 验证点:
673
+
674
+ - contract test 只认 `<pkg>.urls` 作为正式入口
675
+ - `site_runtime`、`platform`、`customer_service` 之间的跨子仓依赖只走 `public` / `api`
676
+ - app 若只是读取另一子仓 settings 的单个值,也必须由对方 `public facade` 暴露,不允许直连 `settings`
677
+ - allowlist 的每次变更都应体现“边界更清晰”而不是“为了过测试临时加白”
678
+
679
+ ### 3.6.4 Route Registry 与冲突裁决契约
680
+
681
+ 必须测试:
682
+
683
+ - `RouteRegistry` 在正式契约中持有 owner / app_id / route hints / replace target 等元数据
684
+ - route conflict 能在真实 `FastAPI.include_router()` 之前被检测
685
+ - 每个 app 的 URL 真相源来自专门的 `<pkg>.urls`,而不是 `apps.py` / `startup()` / 局部 include hook
686
+ - `<pkg>.urls` 必须直接使用原生 `APIRouter()` / `include_router()` 声明本 app 路由,不再引入第二套 route declaration DSL
687
+ - route registry 的正式真相源来自每个 app 的 `<pkg>.urls` 与统一 registry,而不是注册后的启发式修补
688
+ - app-local `clean_and_reorder_routes()` 不得存在
689
+ - 官方扩展层允许存在唯一的 `clean_and_reorder_routes()` 作为最终 route finalizer,但它只能在所有 app、host route、mount 加载完成后最后执行
690
+ - `ROUTE_CONFLICT_POLICY` 默认失败语义稳定
691
+ - 每个 app 的 `route_hints` 在声明期即固化,不会在后续 apply 阶段读取到错误的外层 app 状态
692
+ - startup 之后不会再通过二次 route repair 改写既有路由语义
693
+ - 目录型页面 canonical URL 统一以 `/` 结尾,文件型 URL 不以 `/` 结尾
694
+ - slash 规范化行为在 host / routing 层具备统一契约,不依赖业务包各自实现
695
+ - canonical、hreflang、sitemap 与 redirect target 输出不会偏离正式尾斜杠规则
696
+
697
+ 验证点:
698
+
699
+ - 路由顺序首先来自声明与规则,最终表可由官方扩展层 finalizer 做统一清理与排序
700
+ - generic route 与 definite route 的优先级形成稳定契约
701
+ - route manifest 与最终 FastAPI 路由表是一致且可审计的
702
+ - app 内部的 URL 聚合方式保持原生、可读、接近 Django `urls.py`
703
+ - slashless 请求到目录型 canonical 的规范化结果稳定且可审计
704
+ - 文件型 URL 不会被错误补成带 `/` 的第二 canonical
705
+
706
+ ### 3.6.5 provider binding / events / worker lifecycle 契约
707
+
708
+ 必须测试:
709
+
710
+ - 内部 provider binding 注册项可 reset / unregister
711
+ - auth owner startup/shutdown 前后 user resolver / admin checker 生命周期对称
712
+ - task dispatch owner business event subscriber 可 detach
713
+ - in-process worker 在 shutdown 后不会残留后台任务
714
+ - settings runtime owner / redirects owner / auth owner 通过 binder/provider binding 注册的能力都具备 owner 与对称清理
715
+ - 未实现 `shutdown()` 的平台 app 不会把 startup 注册的全局 provider 残留到下一次 create_app
716
+ - 资源服务不会在 import `pz.core.resources.file_service` 时就绑定一份全局 settings/path 快照
717
+ - i18n/modules provider 不会在本次 resources merge 之前就基于旧 merged locales 快照提前完成同步
718
+
719
+ 验证点:
720
+
721
+ - 平台 app 生命周期不再留下 process-wide 脏状态
722
+ - provider binding registry、event bus subscriber、worker scheduler 都有明确 owner 和清理路径
723
+ - worker/background loops 不再在运行中反复依赖 `ensure_full_settings()` 修补配置
724
+ - task dispatch owner business events 订阅与 buffer 都具备对称清理路径
725
+ - app startup 失败语义是稳定契约,不依赖某个临时环境变量决定是 warning 还是 fail-fast
726
+
727
+ ### 3.6.6 Event Bus 与 Worker SDK 契约
728
+
729
+ 必须测试:
730
+
731
+ - 默认 event bus 可 reset
732
+ - auth owner email events 订阅可对称解绑
733
+ - task dispatch owner business events wildcard 订阅可清理
734
+ - worker SDK 在非 loopback / 分离部署场景下不依赖本地 socket 探测
735
+ - worker SDK 不把固定成功的 Web `/ready` 当作任务系统真实可用性证明
736
+ - `publish_nowait()` 在不同 loop 状态下具有一致、可预测的执行语义,不依赖 `asyncio.run()` / 当前 loop 回退的隐式分支
737
+ - MailFacade 在 provider 不可用、仅日志降级、真正投递成功三种场景下语义可区分
738
+ - mail provider 备用链若存在,其顺序、启用条件与最终诊断结果可测试,不会对调用方伪装成统一成功
739
+
740
+ 验证点:
741
+
742
+ - process-wide event bus 不再残留跨 app/跨测试订阅
743
+ - worker runtime 与 Web runtime 可以独立部署而不互相耦合本地网络前置条件
744
+ - task dispatch owner worker 热路径不会在每次 pull/register/heartbeat 时重新触发全局 settings 补挂
745
+ - task dispatch Web readiness 不再固定成功,且 worker SDK 不依赖 loopback socket 探测
746
+ - worker `wid/token`、TTL、stale_seconds、`re-register` 语义形成稳定协议,过期或权限失效不会被旧分支静默掩盖
747
+ - worker supervisor 的 host-local lock / state / temp 路径来自 `runtime_dir`,而不是系统级临时目录
748
+ - 若代码里仍残留 `401 -> 自动 re-register`,其行为、边界与诊断输出必须可测试,且不会把协议错误伪装成成功恢复
749
+ - `publish_nowait()` 在无运行中 loop 时不会偷偷改成同步执行,也不会靠 `asyncio.run()` 改变上下文语义
750
+ - fire-and-forget 事件处理失败时有稳定 owner/topic/handler 诊断,而不是只留下不可归属的 warning
751
+ - mail send/enqueue 失败不会只写日志后向上层返回“看似成功”的结果
752
+
753
+ ### 3.6.7 ORM 配置与迁移快照契约
754
+
755
+ 必须测试:
756
+
757
+ - `aerich_config` 不在模块导入时固化全局 `TORTOISE_ORM`
758
+ - ORM config 基于当前 project context 惰性构建
759
+ - DB config 不会在内部再次重建一套与入口分叉的 installed apps 列表
760
+ - `aerich.models`、`<app>.models`、`<app>.module.models`、`<app>.runtime.models` 的发现规则在显式 app registry 下保持稳定
761
+
762
+ 验证点:
763
+
764
+ - ORM/迁移配置与入口期 app registry 使用同一真相源
765
+ - 不再依赖全局 settings 旧门面或 import-time 快照
766
+
767
+ ### 3.6.8 CLI 入口与项目上下文契约
768
+
769
+ 必须测试:
770
+
771
+ - `init` 类旧入口不得生成 wrapper 或修改 shell rc
772
+ - `git_plugins` 类命令不是正式入口,也不得承载正式子包发现逻辑
773
+ - `db` CLI 不再写 repo-root 或包内 `aerich.ini`
774
+ - `db` CLI 不再通过 `PYTHONPATH` 注入兜底导入项目包
775
+ - `db` CLI 不再依赖 owner heuristic、suffix match、历史 `app` 路径猜测才能决定迁移归属
776
+
777
+ 验证点:
778
+
779
+ - CLI 都围绕显式 project context 工作
780
+ - 包安装场景与源码仓场景共享同一入口语义
781
+
782
+ ### 3.6.9 Observability / Health Surface 契约
783
+
784
+ 必须测试:
785
+
786
+ - 导入 logging/metrics 模块本身不会在 `cwd` 下偷偷创建 `logs/` 或注册不可清理的全局状态
787
+ - logging 输出目录来自 `runtime_dir/logs` 或显式外部配置
788
+ - root logger、bot handlers、metrics registry 在多次 `create_app()` / 多个 `project_dir` 下可 reset 或隔离
789
+ - `/metrics` 仅在显式启用时挂载,且路径/暴露策略可预测
790
+ - `/dev/extensions` / `/dev/routes` 仅在正式 debug/capability 开关下暴露
791
+ - app/worker 可通过统一 facade 注册指标,而不是直接依赖模块级全局 `metrics`
792
+ - Prometheus exporter route 只负责导出,不把 scrape job / dashboard / alert 语义混入框架设置
793
+ - 默认 metrics label 策略不会把高基数或敏感字段写进公共指标
794
+ - 文件日志输出在未显式启用时不会自动创建 `runtime_dir/logs` 或附加文件 handler
795
+ - Feishu / Telegram 这类 bot handler 的线程池、实例缓存、投递队列具有可测试的 shutdown 与背压/丢弃语义,不会无限积压或跨项目复用
796
+
797
+ 验证点:
798
+
799
+ - observability 是 facade 组,底层依赖显式 capability 与 provider 绑定,而不是 import-time 全局副作用
800
+ - health / metrics / dev surface 的暴露边界稳定且可审计
801
+ - `pz` 只提供监控集成边界,不把部署层监控平台职责塞进 core
802
+ - bot/告警通道失败不会阻塞主请求路径,但也不会因“只写日志”而完全失去可审计性
803
+ - logger `MARKDOWN` 渲染能力有正式 renderer 与契约测试,不再依赖匿名字符串替换
804
+ - bot handler 默认并发度来自 settings 真相源或显式环境覆盖,而不是模块级硬编码
805
+
806
+ ### 3.6.10 Capability 与平台 app 契约
807
+
808
+ 必须测试:
809
+
810
+ - app 可以依赖 capability/facade,而不是依赖某个固定官方实现模块
811
+ - 替换平台 app 后,capability registry 仍保持可用
812
+ - 最小安装场景下,缺失某 capability 时错误或降级语义明确
813
+ - app 不直接依赖 `CapabilityRegistry` 内部结构,而是依赖 facade 或稳定 capability contract
814
+
815
+ 验证点:
816
+
817
+ - 平台能力可以替换、拆仓、裁剪
818
+ - app 生态不会被某个官方 app 或独立子包的内部模块路径绑定死
819
+
820
+ ### 3.6.11 apps 目录与装配边界契约
821
+
822
+ 必须测试:
823
+
824
+ - 正式最小子包源码只从标准 `apps.py` 入口发现,目录名/import 包名默认按 `<app_id>` 收敛
825
+ - 容器目录只负责递归发现嵌套最小子包,不再作为运行时源码根
826
+ - 模板、资源、源码定位逻辑不会依赖历史集合层目录、旧 `distributions/*` 或集合层包的 `__file__`
827
+ - 测试与构建脚本不会把集合层目录或历史分发目录假定为正式源码真相源
828
+ - 官方 app 若允许被其他包 import,也只能通过文档声明的稳定 `public.py facade` 依赖面
829
+
830
+ 验证点:
831
+
832
+ - `pz` 与 `pz_apps` 只承担源码组织职责,正式 app 真相源始终是各自标准 `apps.py` 暴露的 `app.id`
833
+ - 官方维护最小子包与其它最小子包都按同一运行时规则治理;跨包协作默认只通过 facade/API/events/CLI 等稳定交互面
834
+ - 宿主测试若要消费项目 `.sources` 中的共享源码仓,必须通过共享 bootstrap 与显式 import mode 完成,不允许各测试私自改 `sys.path`
835
+
836
+ ### 3.6.12 Packaging / Assets / Lifespan 契约
837
+
838
+ 必须测试:
839
+
840
+ - `pyproject.toml` 实际暴露的 extras 名称与对外文档一致
841
+ - wheel 安装后存在正式 `console_scripts`,不依赖 wrapper/init
842
+ - `register_core()` / `create_app()` 默认不会触发 asset build
843
+ - preflight/asset build 若启用,也只能通过显式 task/CLI 进入
844
+ - lifespan 不会在 startup 后依赖 `clean_and_reorder_routes()` 修补路由 plan
845
+ - settings overrides/L2/default redis config 注册不会在 lifespan 与 infra setup 中重复写入全局状态
846
+
847
+ 验证点:
848
+
849
+ - 包元数据与运行时入口语义一致
850
+ - assets/build 是显式能力,而不是默认装配副作用
851
+ - lifecycle 语义稳定,不靠 startup 后补丁式修复
852
+
853
+ ### 3.6.13 Settings Overrides / ORM Config 契约
854
+
855
+ 必须测试:
856
+
857
+ - `pz.settings` 导入本身不会把最终 env snapshot 写回 `os.environ`
858
+ - overrides/L2 状态在不同 `project_dir`、多次 `create_app()`、并行测试中可隔离或 reset
859
+ - dotted runtime override key 若被声明支持,必须真的能更新嵌套对象
860
+ - 未实现 dotted path 时,会给出明确诊断,而不是静默忽略/半生效
861
+ - `aerich_config` / `TORTOISE_ORM` 不在模块导入时固化全局 settings
862
+ - ORM config 使用入口期 app registry,不再在内部重复 merge `installed_apps + installed_apps_extension`
863
+
864
+ 验证点:
865
+
866
+ - 旧全局 settings 单例不再是进程级隐式真相源;应用层正式读取入口固定为 `settings_center.settings`
867
+ - runtime overrides 与 ORM config 的作用域、输入来源、失败语义都稳定可预测
868
+
869
+ ### 3.6.14 Default App List / App Settings / DB CLI 契约
870
+
871
+ 必须测试:
872
+
873
+ - 最小安装场景下,框架不会默认注入整套官方 app / 独立子包清单与业务包 `app`
874
+ - `installed_apps` / `installed_apps_extension` 的顺序语义在 env 覆盖后仍保持稳定
875
+ - `attach_app_settings()` 不再依赖全局 `pz.settings.settings` / `_collect_env`
876
+ - app settings 聚合正式只包含项目配置中的 `[apps.<app_id>]`、项目 `.env*`、显式环境变量与运行态补值
877
+ - runtime override 白名单不再依赖 `auto_register_overrides_from_obj()` 的对象反射结果
878
+ - `db` CLI 的 owner 归属不再依赖 suffix match / `label == models -> app` 回退规则
879
+ - `db` CLI 不再自动执行 `fix-migrations` 或补丁式修复迁移文件格式
880
+
881
+ 验证点:
882
+
883
+ - 默认 app 清单符合“最小框架包”目标
884
+ - app settings 聚合链是单一且显式的
885
+ - DB CLI 的迁移归属、失败语义、帮助文案都围绕标准包化模型工作
886
+
887
+ ## 4. 资源与包内容测试
888
+
889
+ 必须验证 wheel 中确实包含:
890
+
891
+ - `templates/`
892
+ - `static/`
893
+ - `locales/`
894
+ - `migrations/`
895
+ - `configs/`
896
+ - `configs/__init__.py`
897
+ - `configs/default.jsonc`
898
+ - `configs/default.env`
899
+
900
+ 并验证安装后:
901
+
902
+ - 资源发现可读
903
+ - `pz settings init/update` 可从包内 `configs/default.jsonc` 同步默认配置模板到项目 `pz*.jsonc`
904
+ - `pz settings init/update` 可从包内 `configs/default.env` 合并项目 `.env*` 占位
905
+ - `configs/default.env` 中的变量必须是已由 `configs/default.jsonc` 展示的 password、secret、token、key 类路径;非密钥业务字段不得进入 `.env*` 模板
906
+ - 启动阶段不得直接读取包内 `configs/default.jsonc` 或 `configs/default.env`
907
+ - 包内资源只读不影响运行
908
+
909
+ 补充覆盖:
910
+
911
+ - `static/templates/locales` merge 目标目录都来自当前 project settings
912
+ - `static/templates/locales` merge 必须按 `installed_apps` 从前到后执行;同相对路径资源必须由后面的 app 覆盖前面的 app
913
+ - 不再依赖 `ROOT_DIR`、`app` 或容器 token 或 shared site UI owner 特殊顺序才能得到正确覆盖结果
914
+ - assets build 不再依赖 repo-root 推断或具体最小子包 builder 实现
915
+ - resources bootstrap 不会再隐式触发全局 `module_config_manager.load_configs()` / `translation_manager.reload_translations()`
916
+ - preflight image generation 不再默认写回 `app/static`
917
+ - `ResourcesMixin` / merge order 不得通过 `_repo_root()`、`ROOT_DIR`、自动补包名前缀来决定资源根目录
918
+
919
+ ## 5. CLI 与 worker 测试
920
+
921
+ 必须覆盖:
922
+
923
+ - `pz list`
924
+ - `pz <app> <tool>`
925
+ - `pz db ...`
926
+ - 独立 worker CLI
927
+ - worker 锁路径与状态目录
928
+
929
+ 要求:
930
+
931
+ - `pz db ...` 在 DB 未启用时给出明确诊断
932
+ - worker 与 web 复用同一 settings 解析路径
933
+ - 不再依赖 wrapper 初始化
934
+ - `CliManager` 错误提示与帮助文案不再输出 `python pz ...`
935
+ - `__main__` 在标准入口模式下不再依赖修改 `sys.path` 才能发现项目 app
936
+
937
+ 补充覆盖:
938
+
939
+ - `pz db makemigrations` 导出的 owner 判定必须可预测
940
+ - 不再依赖 suffix match / 历史 `app` 路径猜测这类启发式才能决定迁移归属
941
+ - `CliManager` 不再通过 app_id last-segment 回退或 tool-module direct import 回退掩盖错误配置
942
+ - `aerich_config` / Tortoise config 不会在 import 时固化错误的全局 settings 快照
943
+ - worker SDK 不再默认依赖 loopback socket preflight
944
+ - worker SDK 不会把某个 owner 私有 readiness URL 的宽松响应语义误判成系统 ready
945
+ - `401 -> 自动 re-register` 这条历史恢复路径有明确测试,且不会掩盖正式 session 协议错误
946
+
947
+ ## 5.1 配置中心与资源门面测试
948
+
949
+ 必须覆盖:
950
+
951
+ - settings runtime owner 注册 provider 后可在 shutdown 时清理
952
+ - 如果声明了 settings provider,最终 settings 必须在 app lifecycle startup 前完成解析,不依赖某个具体 app 被特殊提前启动
953
+ - runtime settings 默认值读取不重复触发完整 app 装配副作用
954
+ - `locales` / `static` / `templates` merge 服务在变更 `cache_dir` 后可重建
955
+ - 本地 storage URL 在未配置 base_url 时不会把宿主机路径当公共契约输出
956
+ - `db` CLI 在 wheel 安装模式下不依赖 repo-root 或包内 merged 目录
957
+ - CLI help 文案不再把 `python pz` 或 `python -m pz` 作为标准主路径
958
+ - Web `startup()` / `setup_infra()` / `/ready` 不会替代显式 `pz db ...` 去执行 schema 生成或迁移修补
959
+
960
+ 补充覆盖:
961
+
962
+ - `CorsMixin` 历史环境变量别名是否仍按字段折叠计划工作
963
+ - `I18nMixin` 的默认值、语言代码规范化和派生字段是否由统一 settings 聚合层产出
964
+ - `settings_l2_check_interval` 这类 middleware 配置边界是否仍保持稳定且可测试
965
+ - `register_core()` / `create_app()` 在显式传入 settings snapshot 时不会偷偷回退到全局 settings
966
+ - account / profile owner widget/menu/assets 在 app 替换、多次 create_app、不同 project_dir 下不会残留旧 registry、旧模板环境或旧静态目录绑定
967
+
968
+ ## 6. CI 正式矩阵
969
+
970
+ 至少包含:
971
+
972
+ - Linux 安装 smoke
973
+ - macOS 安装 smoke
974
+ - 纯 base 环境
975
+ - `db + sqlite`
976
+ - `db-postgres`
977
+ - `db-mysql`
978
+ - `redis`
979
+ - wheel 安装模式
980
+
981
+ 执行规则:
982
+
983
+ - SQLite 作为 PR 必跑
984
+ - MySQL、PostgreSQL、Redis、wheel 安装模式进入正式 CI 矩阵,不再只作为口头建议
985
+ - Linux 与 macOS 至少各保留一组非源码目录的 wheel 安装与基础命令 smoke run
986
+
987
+ 补充要求:
988
+
989
+ - 至少保留一组“升级前 -> 升级后 -> 回滚后”验证
990
+ - 至少保留一组 base-only 轻量命令矩阵
991
+
992
+ ## 7. 回归门禁
993
+
994
+ 每完成一组主链改造,应新增相应门禁测试,至少包括:
995
+
996
+ - 一项安装包模式测试
997
+ - 一项可选基础设施测试
998
+ - 一项平台 app / app 替换测试
999
+ - 一项“旧实现行为基线”回归测试,覆盖路由、配置、资源、错误语义或 worker 协议中的至少一个外部可观察行为
1000
+
1001
+ 没有测试的设计项,不应视为完成。