csrd-utils 0.3.85__tar.gz → 0.3.86__tar.gz

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 (178) hide show
  1. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/PKG-INFO +1 -1
  2. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/pyproject.toml +1 -1
  3. csrd_utils-0.3.86/src/csrd_utils/v2/AGENTS.md +349 -0
  4. csrd_utils-0.3.86/src/csrd_utils/v2/compose/__init__.py +93 -0
  5. csrd_utils-0.3.86/src/csrd_utils/v2/compose/infra.py +257 -0
  6. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/v2/compose/loader.py +1 -1
  7. csrd_utils-0.3.85/src/csrd_utils/v2/compose/__init__.py → csrd_utils-0.3.86/src/csrd_utils/v2/compose/operations.py +12 -66
  8. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/v2/compose/renderer.py +117 -64
  9. csrd_utils-0.3.86/src/csrd_utils/v2/compose/scaffolder.py +89 -0
  10. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/v2/generate/handlers.py +262 -251
  11. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/v2/generate/helpers.py +80 -2
  12. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/v2/models/__init__.py +2 -3
  13. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/v2/models/spec.py +7 -9
  14. csrd_utils-0.3.86/src/csrd_utils/v2/models/types.py +6 -0
  15. csrd_utils-0.3.86/src/csrd_utils/v2/templates/service/cookiecutter.json +7 -0
  16. csrd_utils-0.3.86/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/Dockerfile +12 -0
  17. csrd_utils-0.3.86/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/README.md +21 -0
  18. csrd_utils-0.3.86/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/__init__.py +28 -0
  19. csrd_utils-0.3.86/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/requirements.txt +4 -0
  20. csrd_utils-0.3.86/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/settings.py +12 -0
  21. csrd_utils-0.3.86/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/tests/__init__.py +0 -0
  22. csrd_utils-0.3.86/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/tests/conftest.py +12 -0
  23. csrd_utils-0.3.86/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/tests/test_health.py +7 -0
  24. csrd_utils-0.3.86/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/views/__init__.py +9 -0
  25. csrd_utils-0.3.86/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/views/health_view.py +8 -0
  26. csrd_utils-0.3.85/src/csrd_utils/v2/compose/infra.py +0 -178
  27. csrd_utils-0.3.85/src/csrd_utils/v2/compose/spec.py +0 -15
  28. csrd_utils-0.3.85/src/csrd_utils/v2/generate/AGENTS.md +0 -180
  29. csrd_utils-0.3.85/src/csrd_utils/v2/models/types.py +0 -12
  30. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/.gitignore +0 -0
  31. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/README.md +0 -0
  32. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/__init__.py +0 -0
  33. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/__main__.py +0 -0
  34. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/audit.py +0 -0
  35. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/augmentor.py +0 -0
  36. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/cluster.py +0 -0
  37. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/doctor.py +0 -0
  38. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/caching/conftest.fragment.py +0 -0
  39. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/caching/docker-compose.fragment.yaml +0 -0
  40. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/caching/manifest.yaml +0 -0
  41. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/caching/requirements.fragment +0 -0
  42. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/caching/settings.fragment.py +0 -0
  43. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/caching/src/app/cache.py +0 -0
  44. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/caching/tests/test_cache.py +0 -0
  45. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/logging/conftest.fragment.py +0 -0
  46. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/logging/manifest.yaml +0 -0
  47. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/logging/requirements.fragment +0 -0
  48. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/logging/settings.fragment.py +0 -0
  49. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/logging/src/app/middleware/logging.py +0 -0
  50. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/logging/tests/test_logging_middleware.py +0 -0
  51. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/metrics/conftest.fragment.py +0 -0
  52. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/metrics/docker-compose.fragment.yaml +0 -0
  53. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/metrics/manifest.yaml +0 -0
  54. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/metrics/requirements.fragment +0 -0
  55. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/metrics/settings.fragment.py +0 -0
  56. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/metrics/src/app/middleware/metrics.py +0 -0
  57. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/metrics/tests/test_metrics_middleware.py +0 -0
  58. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/tracing/conftest.fragment.py +0 -0
  59. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/tracing/manifest.yaml +0 -0
  60. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/tracing/requirements.fragment +0 -0
  61. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/tracing/settings.fragment.py +0 -0
  62. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/tracing/src/app/middleware/tracing.py +0 -0
  63. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/tracing/tests/test_tracing_middleware.py +0 -0
  64. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/workers/conftest.fragment.py +0 -0
  65. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/workers/docker-compose.fragment.yaml +0 -0
  66. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/workers/manifest.yaml +0 -0
  67. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/workers/requirements.fragment +0 -0
  68. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/workers/settings.fragment.py +0 -0
  69. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/workers/src/app/workers/__init__.py +0 -0
  70. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/workers/src/app/workers/celery_app.py +0 -0
  71. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/workers/src/app/workers/tasks.py +0 -0
  72. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/features/workers/tests/test_workers.py +0 -0
  73. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/fragments.py +0 -0
  74. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/generator.py +0 -0
  75. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/resources.py +0 -0
  76. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/README.md +0 -0
  77. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/cookiecutter.json +0 -0
  78. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/hooks/post_gen_project.py +0 -0
  79. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/.cookiecutterignore +0 -0
  80. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/.dockerignore +0 -0
  81. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/Dockerfile +0 -0
  82. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/README.md +0 -0
  83. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/__init__.py +0 -0
  84. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/cache.py +0 -0
  85. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/delegates/__init__.py +0 -0
  86. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/delegates/upstreams.py +0 -0
  87. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/dependencies/__init__.py +0 -0
  88. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/dependencies/auth_service.py +0 -0
  89. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/dependencies/delegates.py +0 -0
  90. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/dependencies/item_repository.py +0 -0
  91. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/dependencies/worker_broker.py +0 -0
  92. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/docker-compose.yml +0 -0
  93. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/middleware/__init__.py +0 -0
  94. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/middleware/logging.py +0 -0
  95. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/repositories/__init__.py +0 -0
  96. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/repositories/item_repository.py +0 -0
  97. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/requirements.txt +0 -0
  98. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/settings.py +0 -0
  99. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/__init__.py +0 -0
  100. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/acceptance/__init__.py +0 -0
  101. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/acceptance/test_logging_middleware.py +0 -0
  102. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/conftest.py +0 -0
  103. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/test_items.py +0 -0
  104. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/__init__.py +0 -0
  105. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/test_cache.py +0 -0
  106. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/test_database_settings.py +0 -0
  107. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/test_delegates.py +0 -0
  108. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/test_settings_contracts.py +0 -0
  109. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/test_worker_broker.py +0 -0
  110. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/views/__init__.py +0 -0
  111. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/views/unversioned/__init__.py +0 -0
  112. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/views/unversioned/health_view.py +0 -0
  113. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/views/unversioned/items_view.py +0 -0
  114. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/workers/__init__.py +0 -0
  115. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/workers/tasks.py +0 -0
  116. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-app-service/cookiecutter.json +0 -0
  117. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/.dockerignore +0 -0
  118. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/Dockerfile +0 -0
  119. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/README.md +0 -0
  120. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/__init__.py +0 -0
  121. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/delegates/__init__.py +0 -0
  122. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/delegates/auth_delegate.py +0 -0
  123. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/delegates/identity_delegate.py +0 -0
  124. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/dependencies/__init__.py +0 -0
  125. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/dependencies/auth_passthrough.py +0 -0
  126. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/dependencies/auth_service.py +0 -0
  127. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/dependencies/item_repository.py +0 -0
  128. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/models/__init__.py +0 -0
  129. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/models/auth_service.py +0 -0
  130. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/models/items.py +0 -0
  131. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/repositories/__init__.py +0 -0
  132. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/repositories/item_repository.py +0 -0
  133. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/requirements.txt +0 -0
  134. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/settings.py +0 -0
  135. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/views/__init__.py +0 -0
  136. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/views/unversioned/__init__.py +0 -0
  137. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/views/unversioned/auth_service_view.py +0 -0
  138. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/views/unversioned/health_view.py +0 -0
  139. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/views/unversioned/items_view.py +0 -0
  140. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-auth-service/cookiecutter.json +0 -0
  141. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/.dockerignore +0 -0
  142. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/Dockerfile +0 -0
  143. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/README.md +0 -0
  144. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/__init__.py +0 -0
  145. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/dependencies/__init__.py +0 -0
  146. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/models/__init__.py +0 -0
  147. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/models/auth.py +0 -0
  148. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/repositories/__init__.py +0 -0
  149. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/repositories/token_repository.py +0 -0
  150. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/requirements.txt +0 -0
  151. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/settings.py +0 -0
  152. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/views/__init__.py +0 -0
  153. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/views/unversioned/__init__.py +0 -0
  154. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/views/unversioned/auth_view.py +0 -0
  155. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/views/unversioned/health_view.py +0 -0
  156. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-identity-service/cookiecutter.json +0 -0
  157. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/.dockerignore +0 -0
  158. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/Dockerfile +0 -0
  159. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/README.md +0 -0
  160. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/__init__.py +0 -0
  161. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/dependencies/__init__.py +0 -0
  162. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/models/__init__.py +0 -0
  163. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/models/users.py +0 -0
  164. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/repositories/__init__.py +0 -0
  165. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/repositories/user_repository.py +0 -0
  166. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/requirements.txt +0 -0
  167. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/settings.py +0 -0
  168. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/views/__init__.py +0 -0
  169. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/views/unversioned/__init__.py +0 -0
  170. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/views/unversioned/health_view.py +0 -0
  171. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/views/unversioned/users_view.py +0 -0
  172. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/v2/__init__.py +0 -0
  173. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/v2/compose/git.py +0 -0
  174. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/v2/compose/presets.py +0 -0
  175. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/v2/compose/yaml_editor.py +0 -0
  176. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/v2/generate/__init__.py +0 -0
  177. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/v2/generate/menu.py +0 -0
  178. {csrd_utils-0.3.85 → csrd_utils-0.3.86}/src/csrd_utils/v2/models/base.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: csrd-utils
3
- Version: 0.3.85
3
+ Version: 0.3.86
4
4
  Summary: CLI utilities for csrd service generation and feature augmentation
5
5
  Project-URL: Repository, https://github.com/csrd-api/fastapi-common
6
6
  Project-URL: Documentation, https://github.com/csrd-api/fastapi-common/tree/main/packages/utils
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "csrd-utils"
3
- version = "0.3.85"
3
+ version = "0.3.86"
4
4
  description = "CLI utilities for csrd service generation and feature augmentation"
5
5
  license = { text = "MIT" }
6
6
  requires-python = ">=3.12"
@@ -0,0 +1,349 @@
1
+ # Menu System & Compose Design Rules
2
+
3
+ This file captures the design principles of the `v2/generate` menu system
4
+ and the `v2/compose` rendering pipeline.
5
+ Consult it for every change that adds, removes, or modifies menus, menu items,
6
+ prompts, interactive selection flows, or compose/renderer behavior.
7
+
8
+ ## Scope
9
+
10
+ - Applies to `packages/utils/src/csrd_utils/v2/**`.
11
+ - Also follow `packages/utils/AGENTS.md` and the top-level `AGENTS.md`.
12
+
13
+ ## Drop-in replacement intent
14
+
15
+ The `v2` module is intended to **fully replace** the bare `csrd` command.
16
+ Once promoted, the `v2/` directory becomes the top-level implementation and
17
+ the `v2` namespace disappears.
18
+
19
+ **Naming rule**: Do not use "v2" in any module name, class name, function name,
20
+ variable name, constant, test name, CLI label, or user-facing string inside this
21
+ package. The only place "v2" appears is the directory name itself, which will be
22
+ removed at promotion time. Names should read as if `v2/` does not exist —
23
+ e.g. `compose`, `generate`, `ComposeSpec`, `render_workspace`, not
24
+ `v2_compose`, `V2ComposeSpec`, `render_v2_workspace`.
25
+
26
+ ## Architecture overview
27
+
28
+ ### Generate layer (interactive / CLI)
29
+
30
+ The menu system is split into three layers:
31
+
32
+ | Layer | File | Responsibility |
33
+ |---|---|---|
34
+ | **Primitives** | `helpers.py` | `MenuItem` dataclass, rendering, TTY/non-TTY selection, prompt helpers (`prompt_text`, `prompt_yes_no`, `prompt_single_select`, `prompt_multi_select`). |
35
+ | **Orchestration** | `menu.py` | Declarative item lists (`IN_WORKSPACE_ITEMS`, `_build_no_workspace_items`), context predicates, direct-mode dispatch, and the public `run_generate_menu` entry-point. |
36
+ | **Handlers** | `handlers.py` | Business-logic callbacks invoked by menu items. Each returns an `int` exit code. Shared finalization via `_finalize_workspace`. |
37
+
38
+ Changes must respect this separation. Do not put rendering logic in handlers
39
+ or business logic in helpers.
40
+
41
+ ### Compose layer (deterministic rendering)
42
+
43
+ The compose pipeline is split into focused modules:
44
+
45
+ | Module | Responsibility |
46
+ |---|---|
47
+ | `models/spec.py` | Pydantic models: `ComposeSpec`, `ServiceNode`, `InfraNode`, `PresetDefinition`, constants (`INFRA_DATABASES`, `INFRA_CATEGORIES`, etc.) |
48
+ | `compose/infra.py` | `InfraDescriptor` registry — single source of truth for all infra container defs, service env wiring, depends_on, and env example lines |
49
+ | `compose/operations.py` | Workspace mutations: `add_service`, `add_infra`, `remove_infra`, `available_infra`, `configured_infra`, `apply`, `validate` |
50
+ | `compose/renderer.py` | Pure `spec → file content` transforms: docker-compose.yml, .env.example, README, .gitignore, pyproject.toml, workspace marker |
51
+ | `compose/scaffolder.py` | Cookiecutter template → workspace filesystem layout (idempotent) |
52
+ | `compose/loader.py` | Load/save `csrd-compose.yaml` |
53
+ | `compose/presets.py` | Preset definitions and category helpers |
54
+ | `compose/yaml_editor.py` | YAML serialization utilities |
55
+ | `compose/__init__.py` | Re-exports only — no business logic |
56
+
57
+ ## Non-negotiable design rules
58
+
59
+ ### 1. Deterministic rendering
60
+
61
+ Every render function must be a **pure transform from spec to output**.
62
+ Same `ComposeSpec` → same files, byte-for-byte.
63
+
64
+ - **No randomness**: no UUIDs, timestamps, random ports, or non-deterministic ordering.
65
+ - **No environment reads**: renderers must not read env vars, `$HOME`, `os.getcwd()`, or any external state.
66
+ - **Stable iteration**: `spec.infra` and `spec.services` are ordered lists (not sets). Compose services appear in insertion order.
67
+ - **Stable YAML**: `dumps_yaml` uses `default_flow_style=False` for consistent formatting.
68
+ - **Test proof**: `test_render_workspace_is_idempotent` asserts two consecutive renders produce byte-identical output.
69
+
70
+ ### 2. Declarative spec as single source of truth
71
+
72
+ `csrd-compose.yaml` (`ComposeSpec`) is the **only** source of truth for a workspace.
73
+
74
+ - Renderers never inspect generated files to decide what to generate.
75
+ - Data flow is one-directional: `user input → operations (mutate spec) → renderer (spec → files)`.
76
+ - No feedback loops: the renderer does not read `docker-compose.yml` or scaffolded source to inform its output.
77
+
78
+ ### 3. Data-driven infra registry (DRY / Open-Closed)
79
+
80
+ All infra knowledge lives in `INFRA_REGISTRY` (`infra.py`) as frozen `InfraDescriptor` instances.
81
+
82
+ - **Adding a new infra type** requires only adding a new `InfraDescriptor` entry to the registry.
83
+ - Do **not** scatter infra-specific if/elif branches across renderer, env example, or service wiring code.
84
+ - The renderer queries the registry generically via `desc.service_env`, `desc.depends_on`, `desc.env_example_lines`.
85
+ - Exception: sqlite is special-cased in `_wire_feature` because it needs per-service volumes with no shared container. This is documented inline.
86
+
87
+ ### 4. Single Responsibility (SRP)
88
+
89
+ - `compose/__init__.py` — re-exports only, zero business logic.
90
+ - `operations.py` — spec mutations (add/remove on disk).
91
+ - `renderer.py` — spec → file content (no I/O decisions, no spec mutations).
92
+ - `scaffolder.py` — cookiecutter execution and filesystem rearrangement.
93
+ - `infra.py` — infra data and lookup helpers.
94
+ - `handlers.py` — interactive/direct CLI handlers. Shared finalization via `_finalize_workspace`.
95
+
96
+ Do not mix responsibilities across these modules.
97
+
98
+ ### 5. DRY: No duplicate definitions
99
+
100
+ - **Infra wiring** (service env, depends_on, env example) is defined once in `InfraDescriptor`, not repeated in renderer, env builder, or handlers.
101
+ - **Workspace finalization** (create dir → add infra → add services → render) is in `_finalize_workspace`, shared by both `create_workspace` and `create_workspace_from_preset`.
102
+ - **DB detection** is in `infra.detect_configured_db`, not duplicated in renderer and handlers.
103
+ - **Model types** are defined in `models/spec.py`, re-exported through `models/__init__.py` and `compose/__init__.py` — no shim modules or alias files.
104
+
105
+ ### 6. Idempotent scaffolding
106
+
107
+ `scaffold_service` skips silently if `src/<service-name>/` already exists.
108
+ User edits to scaffolded files are never overwritten on re-render.
109
+
110
+ - `.env` is created only if missing (user customizations preserved).
111
+ - All other workspace files (compose, env example, pyproject, README, gitignore) are overwritten on every render (they are derived from spec).
112
+
113
+ ### 7. Workspace filesystem contract
114
+
115
+ ```
116
+ <ws-root>/
117
+ csrd-compose.yaml ← spec (source of truth)
118
+ docker-compose.yml ← rendered from spec
119
+ .env.example ← rendered from spec
120
+ .env ← created once, never overwritten
121
+ pyproject.toml ← rendered from spec (pytest config)
122
+ README.md ← rendered from spec
123
+ .gitignore ← rendered from spec
124
+ .csrd-workspace ← marker file
125
+ Dockerfile.<service-name> ← one per service, at root
126
+ src/<service-name>/ ← service source (kebab-case dir)
127
+ tests/unit/<service_snake>/ ← unit tests (snake_case dir)
128
+ tests/acceptance/<service_snake>/ ← acceptance stubs (snake_case dir)
129
+ ```
130
+
131
+ - Service source dirs use **kebab-case** (matching `service.name`).
132
+ - Test dirs use **snake_case** (valid Python package names).
133
+ - Dockerfiles use kebab-case service name for the suffix, snake_case for the Python module reference in CMD.
134
+
135
+ ### 8. Consistent model paradigm
136
+
137
+ All models use **Pydantic `BaseModel`** (from `models/base.py`).
138
+ Do not use `@dataclass` for spec or preset models.
139
+ Use `model_config = ConfigDict(frozen=True)` for immutable models (e.g. `PresetDefinition`).
140
+
141
+ Exception: `InfraDescriptor` in `infra.py` uses `@dataclass(frozen=True)` because it is a
142
+ compose-internal data structure, not a serializable spec model.
143
+
144
+ ### 9. Exception semantics
145
+
146
+ - `FileNotFoundError` — missing spec file (`loader.py`).
147
+ - `ValueError` — invalid spec content, duplicate service/infra, unconfigured infra removal.
148
+ - `KeyError` — unknown infra type in registry lookup.
149
+
150
+ Do not use generic `Exception` or mismatched exception types.
151
+
152
+ ### 10. Declarative item definitions
153
+
154
+ Menu items are declared as static `list[MenuItem]` constants (or built by a
155
+ simple factory when runtime data like `default_name` is needed).
156
+
157
+ - **Do not** build menus with imperative if/else chains.
158
+ - Use the `visible` predicate on `MenuItem` to hide items based on context.
159
+ The `filter_visible()` helper evaluates these at menu-build time.
160
+
161
+ ### 11. Dual-mode: interactive + direct dispatch
162
+
163
+ Every interactive flow must have a non-interactive (direct) counterpart
164
+ invokable via CLI flags (`target`, `--name`, `--database`, etc.).
165
+
166
+ - `run_generate_menu` routes direct targets first, then falls through to the
167
+ interactive menu. Keep this order: direct dispatch → interactive.
168
+ - Direct handlers are named `*_direct`, interactive ones `*_interactive`.
169
+
170
+ ### 12. TTY / non-TTY graceful degradation
171
+
172
+ All interactive primitives must work in both TTY and non-TTY environments:
173
+
174
+ - **TTY path**: arrow-key navigation via `cbreak_mode()`, `>` cursor marker.
175
+ - **Non-TTY path**: numbered list with text input fallback.
176
+ - Gate on `sys.stdin.isatty() and sys.stdout.isatty()` — never assume a terminal.
177
+ - `cbreak_mode()` yields `None` when `termios` is unavailable; always handle that.
178
+ - Escape key detection uses `select.select()` with a 50 ms timeout via
179
+ `_read_escape_seq()` to distinguish standalone Escape from arrow-key
180
+ prefixes (`\x1b[A`/`\x1b[B`).
181
+
182
+ ### 13. Cancel, interrupt, and back-navigation
183
+
184
+ **Ctrl-C (KeyboardInterrupt)**:
185
+ - Must **propagate** from all prompt primitives — they must NOT swallow it.
186
+ - Only the outermost handler or `run_menu` catches it.
187
+ - Handlers print `"Cancelled."` (wrapped in `contextlib.suppress`) and
188
+ return exit code `1`.
189
+ - Swallowing at the primitive level causes fall-through — this is a bug.
190
+
191
+ **Escape / back (GoBack)**:
192
+ - `GoBack` is a lightweight exception raised by `prompt_single_select` and
193
+ `prompt_multi_select` when the user presses Escape (TTY) or types `0` /
194
+ `back` (non-TTY).
195
+ - In **nested flows** (e.g. `_prompt_infra_categories`), catch `GoBack`
196
+ from sub-selects and loop back to the parent prompt.
197
+ - In **top-level handlers**, catch `GoBack` and skip the step (empty selection).
198
+ - In **`run_menu`**, Escape = cancel (same as selecting the cancel item).
199
+ - Visual hints: `"Esc back"` (TTY), `"0) back"` (non-TTY).
200
+
201
+ **EOF (EOFError)**:
202
+ - Return quietly with the default/empty value from primitives.
203
+ - `run_menu` returns `0`.
204
+
205
+ ### 14. Exit code contract
206
+
207
+ All handlers and menu runners return an `int` exit code:
208
+
209
+ | Code | Meaning |
210
+ |---|---|
211
+ | `0` | Success or clean cancel |
212
+ | `1` | Error or user interrupt |
213
+
214
+ Do not raise exceptions to signal user-facing outcomes; print and return a code.
215
+
216
+ ### 15. Prompt conventions
217
+
218
+ - `prompt_text(question, default=...)` — returns default on empty input or EOF.
219
+ In TTY mode uses `_tty_line_input` (cbreak-mode character-by-character
220
+ reading) so that Escape raises `GoBack` and Ctrl-C raises
221
+ `KeyboardInterrupt`. Non-TTY falls back to `input()`.
222
+ - `prompt_yes_no(question, default=True)` — same TTY/non-TTY split. First
223
+ prompt defaults yes; "add another?" prompts default no.
224
+ - `prompt_single_select` — auto-selects when only one option exists. Escape
225
+ raises `GoBack`.
226
+ - `prompt_multi_select` — spacebar toggles, Enter confirms; non-TTY accepts
227
+ comma-separated numbers, `all`, or `back`/`0`. Escape raises `GoBack`.
228
+
229
+ All four prompts support Escape for back-navigation. Do not invent new prompt
230
+ primitives without adding them to `helpers.py` first.
231
+
232
+ ### 16. Composable wizard flows
233
+
234
+ Multi-step interactive flows **must** use `run_wizard()` from `helpers.py`
235
+ with a list of `WizardStep` callables. Each step is a function that receives
236
+ a shared `state: dict[str, object]` and either returns normally (advance) or
237
+ raises `GoBack` (go back). The wizard handles all back-navigation and
238
+ interrupt logic automatically.
239
+
240
+ ```python
241
+ # ✅ Correct — composable steps, no control flow boilerplate
242
+ steps: list[WizardStep] = [
243
+ _step_workspace_name,
244
+ _step_infra_categories,
245
+ _step_services,
246
+ _step_git_init,
247
+ ]
248
+ state = run_wizard(steps)
249
+ ```
250
+
251
+ ```python
252
+ # ❌ Prohibited — hand-coded if/elif step dispatch
253
+ while step <= 3:
254
+ if step == 0:
255
+ ...
256
+ elif step == 1:
257
+ ...
258
+ ```
259
+
260
+ Rules:
261
+ - **Never** use raw `while step <= N` / `if step == 0` dispatch in handlers.
262
+ - Each step must be an independent `_step_*` function in `handlers.py`.
263
+ - Steps that appear in more than one wizard flow must be shared (e.g.
264
+ `_step_workspace_name`, `_step_git_init`).
265
+ - Steps communicate exclusively through the shared state dict.
266
+ - `GoBack` at step 0 = cancel (exit code 0).
267
+ - `KeyboardInterrupt` at any step = abort (exit code 1).
268
+
269
+ Back-navigation semantics:
270
+ ```
271
+ Step 0: name ← Escape here = cancel (exit code 0)
272
+ Step 1: infra ← Escape here = back to step 0
273
+ Step 2: services ← Escape here = back to step 1
274
+ Step 3: git init ← Escape here = back to step 2
275
+ ```
276
+
277
+
278
+ ### 17. Naming & normalization
279
+
280
+ - Service names: `normalize_service_name()` → kebab-case, appends `-service`.
281
+ - Workspace names: `resolve_workspace_name()` → collapse whitespace, reject
282
+ path separators.
283
+ - Always print a `"Normalized to: ..."` message when the name changes.
284
+
285
+ ### 18. Category-based infra selection
286
+
287
+ Infrastructure is organized by category (`database`, `messaging`, `caching`)
288
+ defined in `models/spec.py` via `INFRA_CATEGORIES`.
289
+
290
+ - Step 1: multi-select categories.
291
+ - Step 2: for each category, single-select the specific type (auto-select if
292
+ only one member).
293
+ - Already-configured types are excluded from the available set.
294
+
295
+ Do not bypass the category system with flat infra-type lists.
296
+
297
+ ### 19. Context awareness
298
+
299
+ Menus adapt based on workspace presence:
300
+
301
+ - **Inside workspace** (`csrd-compose.yaml` exists): show `add service`,
302
+ `add infra` (if slots remain), `remove infra` (if any configured), `cancel`.
303
+ - **Outside workspace**: show `custom`, `preset`, `cancel`.
304
+
305
+ Context predicates (`_has_workspace`, `_has_available_infra`,
306
+ `_has_configured_infra`) must be pure filesystem checks — no global state,
307
+ no caching.
308
+
309
+ Alias uniqueness: each alias must be unique within a menu list. Do not reuse
310
+ a shortcut alias across items in the same list (e.g. `"c"` for both `custom`
311
+ and `cancel`).
312
+
313
+ ### 20. No runtime auto-discovery
314
+
315
+ Menu items, infra types, and preset definitions are statically registered.
316
+ Do not add plugin loaders, dynamic module scanning, or entry-point discovery.
317
+ This aligns with the top-level `AGENTS.md` cookiecutter guardrail.
318
+
319
+ ## Audit checklist
320
+
321
+ When asked to "audit the menus or compose system", verify each of the following:
322
+
323
+ ### Menu audit
324
+ 1. Every `MenuItem` has a `label`, at least two `aliases` (full name + shortcut), and a handler.
325
+ 2. Every menu list ends with a `cancel` item.
326
+ 3. `filter_visible()` is called before passing items to `run_menu()`.
327
+ 4. All interactive flows have a `*_direct` counterpart wired in `run_generate_menu`.
328
+ 5. TTY and non-TTY paths are both exercised (check for `isatty` gates).
329
+ 6. `KeyboardInterrupt` and `EOFError` are caught in every prompt/menu call.
330
+ 7. Exit codes follow the `0`/`1` contract.
331
+ 8. Infra selection uses the category flow, not a flat list.
332
+ 9. Names are normalized and the user is informed of changes.
333
+ 10. No new prompt primitives exist outside `helpers.py`.
334
+ 11. The three-layer separation (helpers → menu → handlers) is preserved.
335
+ 12. Tests in `tests/test_cli.py` and `tests/test_v2_generate*.py` cover new/changed items.
336
+ 13. All multi-step interactive flows use `run_wizard()` with a list of `WizardStep` callables — no raw `while step <= N` / `if step == 0` dispatch exists in handlers.
337
+ 14. No step logic is duplicated across wizard flows — shared steps (`_step_workspace_name`, `_step_git_init`, etc.) are extracted to `_step_*` functions.
338
+
339
+ ### Compose / renderer audit
340
+ 15. All render functions are pure `spec → string` transforms with no side effects, no env reads, no filesystem inspection.
341
+ 16. `test_render_workspace_is_idempotent` passes — two consecutive renders produce identical output.
342
+ 17. Infra wiring uses `INFRA_REGISTRY` descriptors — no hardcoded if/elif branches (except the documented sqlite special case).
343
+ 18. `.env` is created-if-missing, all other files are overwritten from spec.
344
+ 19. `compose/__init__.py` contains only re-exports — no business logic.
345
+ 20. `operations.py` functions load → mutate → save spec — no rendering.
346
+ 21. Workspace finalization uses `_finalize_workspace` — no duplicated create/add/render sequences in handlers.
347
+ 22. `detect_configured_db` is defined once in `infra.py` — not duplicated.
348
+ 23. Test dirs use snake_case, source dirs use kebab-case, Dockerfiles use kebab-case suffix.
349
+ 24. No shim modules or alias re-export files exist (pre-1.0 refactor policy).
@@ -0,0 +1,93 @@
1
+ """Compose package namespace — re-exports only, no business logic."""
2
+
3
+ # Re-export model types for convenience
4
+ from ..models import (
5
+ INFRA_ALL_TYPES,
6
+ INFRA_CATEGORIES,
7
+ INFRA_DATABASES,
8
+ ComposeSpec,
9
+ InfraNode,
10
+ PresetDefinition,
11
+ ServiceNode,
12
+ )
13
+ from .infra import (
14
+ INFRA_REGISTRY,
15
+ INFRA_RENDERERS,
16
+ InfraDescriptor,
17
+ descriptor_for,
18
+ descriptors_for_feature,
19
+ detect_configured_db,
20
+ render_infra,
21
+ )
22
+ from .loader import (
23
+ SPEC_FILENAME,
24
+ default_spec,
25
+ load_spec,
26
+ save_spec,
27
+ spec_file_path,
28
+ )
29
+ from .operations import (
30
+ add_infra,
31
+ add_service,
32
+ apply,
33
+ available_infra,
34
+ configured_infra,
35
+ remove_infra,
36
+ validate,
37
+ )
38
+ from .presets import (
39
+ PRESET_REGISTRY,
40
+ list_presets,
41
+ members_for_category,
42
+ )
43
+ from .renderer import render_workspace
44
+ from .scaffolder import scaffold_service, scaffold_services
45
+ from .yaml_editor import (
46
+ deep_merge,
47
+ dumps_yaml,
48
+ ensure_list_item,
49
+ load_yaml,
50
+ save_yaml,
51
+ set_key,
52
+ )
53
+
54
+ __all__ = [
55
+ "INFRA_ALL_TYPES",
56
+ "INFRA_CATEGORIES",
57
+ "INFRA_DATABASES",
58
+ "INFRA_REGISTRY",
59
+ "INFRA_RENDERERS",
60
+ "PRESET_REGISTRY",
61
+ "SPEC_FILENAME",
62
+ "ComposeSpec",
63
+ "InfraDescriptor",
64
+ "InfraNode",
65
+ "PresetDefinition",
66
+ "ServiceNode",
67
+ "add_infra",
68
+ "add_service",
69
+ "apply",
70
+ "available_infra",
71
+ "configured_infra",
72
+ "deep_merge",
73
+ "default_spec",
74
+ "descriptor_for",
75
+ "descriptors_for_feature",
76
+ "detect_configured_db",
77
+ "dumps_yaml",
78
+ "ensure_list_item",
79
+ "list_presets",
80
+ "load_spec",
81
+ "load_yaml",
82
+ "members_for_category",
83
+ "remove_infra",
84
+ "render_infra",
85
+ "render_workspace",
86
+ "save_spec",
87
+ "save_yaml",
88
+ "scaffold_service",
89
+ "scaffold_services",
90
+ "set_key",
91
+ "spec_file_path",
92
+ "validate",
93
+ ]