nene2-python 1.8.8__tar.gz → 1.8.9__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 (238) hide show
  1. {nene2_python-1.8.8 → nene2_python-1.8.9}/CHANGELOG.md +12 -0
  2. {nene2_python-1.8.8 → nene2_python-1.8.9}/PKG-INFO +1 -1
  3. nene2_python-1.8.9/docs/field-trials/2026-05-field-trial-46.md +128 -0
  4. nene2_python-1.8.9/docs/field-trials/2026-05-field-trial-47.md +93 -0
  5. nene2_python-1.8.9/docs/field-trials/2026-05-field-trial-48.md +96 -0
  6. nene2_python-1.8.9/docs/field-trials/2026-05-field-trial-49.md +96 -0
  7. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/how-to/run-tests.md +11 -0
  8. {nene2_python-1.8.8 → nene2_python-1.8.9}/pyproject.toml +1 -1
  9. {nene2_python-1.8.8 → nene2_python-1.8.9}/uv.lock +1 -1
  10. {nene2_python-1.8.8 → nene2_python-1.8.9}/.env.example +0 -0
  11. {nene2_python-1.8.8 → nene2_python-1.8.9}/.github/workflows/ci.yml +0 -0
  12. {nene2_python-1.8.8 → nene2_python-1.8.9}/.github/workflows/docs.yml +0 -0
  13. {nene2_python-1.8.8 → nene2_python-1.8.9}/.github/workflows/publish.yml +0 -0
  14. {nene2_python-1.8.8 → nene2_python-1.8.9}/.gitignore +0 -0
  15. {nene2_python-1.8.8 → nene2_python-1.8.9}/.vitepress/config.mts +0 -0
  16. {nene2_python-1.8.8 → nene2_python-1.8.9}/.vitepress/theme/custom.css +0 -0
  17. {nene2_python-1.8.8 → nene2_python-1.8.9}/.vitepress/theme/index.ts +0 -0
  18. {nene2_python-1.8.8 → nene2_python-1.8.9}/AGENTS.md +0 -0
  19. {nene2_python-1.8.8 → nene2_python-1.8.9}/CLAUDE.md +0 -0
  20. {nene2_python-1.8.8 → nene2_python-1.8.9}/Dockerfile +0 -0
  21. {nene2_python-1.8.8 → nene2_python-1.8.9}/LICENSE +0 -0
  22. {nene2_python-1.8.8 → nene2_python-1.8.9}/README.md +0 -0
  23. {nene2_python-1.8.8 → nene2_python-1.8.9}/alembic/README +0 -0
  24. {nene2_python-1.8.8 → nene2_python-1.8.9}/alembic/env.py +0 -0
  25. {nene2_python-1.8.8 → nene2_python-1.8.9}/alembic/script.py.mako +0 -0
  26. {nene2_python-1.8.8 → nene2_python-1.8.9}/alembic/versions/001_create_notes_and_tags_tables.py +0 -0
  27. {nene2_python-1.8.8 → nene2_python-1.8.9}/alembic.ini +0 -0
  28. {nene2_python-1.8.8 → nene2_python-1.8.9}/compose.yaml +0 -0
  29. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/adr/0001-toolchain.md +0 -0
  30. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/adr/0002-clean-architecture.md +0 -0
  31. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/adr/0003-security-first.md +0 -0
  32. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/adr/0004-ai-first-design.md +0 -0
  33. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/adr/0005-logging.md +0 -0
  34. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/adr/0006-rate-limiting.md +0 -0
  35. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/adr/0009-mcp-design.md +0 -0
  36. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/adr/0010-async-use-case.md +0 -0
  37. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/adr/0011-mcp-as-core-dependency.md +0 -0
  38. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/de/index.md +0 -0
  39. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/de/tutorials/getting-started.md +0 -0
  40. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/explanation/architecture.md +0 -0
  41. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/explanation/design-philosophy.md +0 -0
  42. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-1.md +0 -0
  43. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-10.md +0 -0
  44. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-11.md +0 -0
  45. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-12.md +0 -0
  46. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-13.md +0 -0
  47. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-14.md +0 -0
  48. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-15.md +0 -0
  49. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-16.md +0 -0
  50. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-17.md +0 -0
  51. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-18.md +0 -0
  52. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-19.md +0 -0
  53. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-2.md +0 -0
  54. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-20.md +0 -0
  55. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-21.md +0 -0
  56. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-22.md +0 -0
  57. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-23.md +0 -0
  58. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-24.md +0 -0
  59. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-25.md +0 -0
  60. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-26.md +0 -0
  61. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-27.md +0 -0
  62. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-28.md +0 -0
  63. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-29.md +0 -0
  64. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-3.md +0 -0
  65. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-30.md +0 -0
  66. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-31.md +0 -0
  67. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-32.md +0 -0
  68. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-33.md +0 -0
  69. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-34.md +0 -0
  70. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-35.md +0 -0
  71. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-36.md +0 -0
  72. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-37.md +0 -0
  73. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-38.md +0 -0
  74. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-39.md +0 -0
  75. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-4.md +0 -0
  76. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-40.md +0 -0
  77. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-41.md +0 -0
  78. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-42.md +0 -0
  79. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-43.md +0 -0
  80. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-44.md +0 -0
  81. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-45.md +0 -0
  82. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-5.md +0 -0
  83. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-6.md +0 -0
  84. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-7.md +0 -0
  85. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-8.md +0 -0
  86. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/field-trials/2026-05-field-trial-9.md +0 -0
  87. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/fr/index.md +0 -0
  88. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/fr/tutorials/getting-started.md +0 -0
  89. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/how-to/add-new-domain.md +0 -0
  90. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/how-to/async-use-case.md +0 -0
  91. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/how-to/configure-auth.md +0 -0
  92. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/how-to/new-project.md +0 -0
  93. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/how-to/problem-details.md +0 -0
  94. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/how-to/sqlalchemy-repository.md +0 -0
  95. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/how-to/validation.md +0 -0
  96. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/howto/mcp-setup.md +0 -0
  97. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/index.md +0 -0
  98. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/ja/explanation/architecture.md +0 -0
  99. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/ja/explanation/design-philosophy.md +0 -0
  100. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/ja/how-to/add-new-domain.md +0 -0
  101. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/ja/how-to/configure-auth.md +0 -0
  102. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/ja/how-to/new-project.md +0 -0
  103. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/ja/how-to/run-tests.md +0 -0
  104. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/ja/how-to/sqlalchemy-repository.md +0 -0
  105. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/ja/howto/mcp-setup.md +0 -0
  106. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/ja/index.md +0 -0
  107. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/ja/reference/api.md +0 -0
  108. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/ja/reference/configuration.md +0 -0
  109. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/ja/reference/framework-modules.md +0 -0
  110. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/ja/tutorials/first-domain.md +0 -0
  111. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/ja/tutorials/getting-started.md +0 -0
  112. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/pt-br/index.md +0 -0
  113. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/pt-br/tutorials/getting-started.md +0 -0
  114. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/reference/api.md +0 -0
  115. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/reference/configuration.md +0 -0
  116. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/reference/framework-modules.md +0 -0
  117. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/roadmap.md +0 -0
  118. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/todo/current.md +0 -0
  119. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/tutorials/first-domain.md +0 -0
  120. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/tutorials/getting-started.md +0 -0
  121. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/zh/index.md +0 -0
  122. {nene2_python-1.8.8 → nene2_python-1.8.9}/docs/zh/tutorials/getting-started.md +0 -0
  123. {nene2_python-1.8.8 → nene2_python-1.8.9}/package-lock.json +0 -0
  124. {nene2_python-1.8.8 → nene2_python-1.8.9}/package.json +0 -0
  125. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/__init__.py +0 -0
  126. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/__main__.py +0 -0
  127. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/app.py +0 -0
  128. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/comment/__init__.py +0 -0
  129. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/comment/entity.py +0 -0
  130. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/comment/exceptions.py +0 -0
  131. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/comment/handler.py +0 -0
  132. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/comment/repository.py +0 -0
  133. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/comment/sqlalchemy_repository.py +0 -0
  134. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/comment/use_case.py +0 -0
  135. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/mcp.py +0 -0
  136. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/note/__init__.py +0 -0
  137. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/note/async_use_case.py +0 -0
  138. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/note/entity.py +0 -0
  139. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/note/exceptions.py +0 -0
  140. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/note/handler.py +0 -0
  141. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/note/repository.py +0 -0
  142. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/note/sqlalchemy_repository.py +0 -0
  143. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/note/use_case.py +0 -0
  144. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/schema.py +0 -0
  145. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/tag/__init__.py +0 -0
  146. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/tag/entity.py +0 -0
  147. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/tag/exceptions.py +0 -0
  148. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/tag/handler.py +0 -0
  149. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/tag/repository.py +0 -0
  150. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/tag/sqlalchemy_repository.py +0 -0
  151. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/example/tag/use_case.py +0 -0
  152. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/__init__.py +0 -0
  153. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/auth/__init__.py +0 -0
  154. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/auth/api_key.py +0 -0
  155. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/auth/bearer_token.py +0 -0
  156. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/auth/exceptions.py +0 -0
  157. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/auth/interfaces.py +0 -0
  158. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/auth/local_verifier.py +0 -0
  159. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/config/__init__.py +0 -0
  160. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/config/settings.py +0 -0
  161. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/database/__init__.py +0 -0
  162. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/database/exceptions.py +0 -0
  163. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/database/health.py +0 -0
  164. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/database/interfaces.py +0 -0
  165. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/database/sqlalchemy_executor.py +0 -0
  166. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/database/utils.py +0 -0
  167. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/http/__init__.py +0 -0
  168. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/http/health.py +0 -0
  169. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/http/pagination.py +0 -0
  170. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/http/problem_details.py +0 -0
  171. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/log/__init__.py +0 -0
  172. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/log/setup.py +0 -0
  173. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/mcp/__init__.py +0 -0
  174. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/mcp/http_client.py +0 -0
  175. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/mcp/server.py +0 -0
  176. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/middleware/__init__.py +0 -0
  177. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/middleware/domain_exception.py +0 -0
  178. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/middleware/error_handler.py +0 -0
  179. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/middleware/request_id.py +0 -0
  180. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/middleware/request_logging.py +0 -0
  181. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/middleware/request_size_limit.py +0 -0
  182. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/middleware/security_headers.py +0 -0
  183. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/middleware/throttle.py +0 -0
  184. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/py.typed +0 -0
  185. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/use_case/__init__.py +0 -0
  186. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/use_case/protocols.py +0 -0
  187. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/validation/__init__.py +0 -0
  188. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/nene2/validation/exceptions.py +0 -0
  189. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/scripts/__init__.py +0 -0
  190. {nene2_python-1.8.8 → nene2_python-1.8.9}/src/scripts/export_openapi.py +0 -0
  191. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/__init__.py +0 -0
  192. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/example/__init__.py +0 -0
  193. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/example/comment/__init__.py +0 -0
  194. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/example/comment/test_comment_http.py +0 -0
  195. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/example/comment/test_comment_repository.py +0 -0
  196. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/example/comment/test_comment_use_case.py +0 -0
  197. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/example/conftest.py +0 -0
  198. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/example/note/__init__.py +0 -0
  199. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/example/note/test_async_note_use_case.py +0 -0
  200. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/example/note/test_list_notes.py +0 -0
  201. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/example/note/test_note_repository.py +0 -0
  202. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/example/tag/__init__.py +0 -0
  203. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/example/tag/test_tag_repository.py +0 -0
  204. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/example/tag/test_tags.py +0 -0
  205. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/example/test_cors.py +0 -0
  206. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/example/test_mcp.py +0 -0
  207. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/__init__.py +0 -0
  208. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/auth/__init__.py +0 -0
  209. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/auth/test_api_key.py +0 -0
  210. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/auth/test_bearer_token.py +0 -0
  211. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/auth/test_token_issuer.py +0 -0
  212. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/config/__init__.py +0 -0
  213. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/config/test_settings.py +0 -0
  214. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/database/__init__.py +0 -0
  215. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/database/test_transaction.py +0 -0
  216. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/database/test_utils.py +0 -0
  217. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/http/__init__.py +0 -0
  218. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/http/test_health.py +0 -0
  219. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/http/test_pagination.py +0 -0
  220. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/http/test_problem_details.py +0 -0
  221. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/log/__init__.py +0 -0
  222. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/log/test_setup.py +0 -0
  223. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/mcp/__init__.py +0 -0
  224. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/mcp/test_http_client.py +0 -0
  225. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/middleware/__init__.py +0 -0
  226. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/middleware/test_error_handler.py +0 -0
  227. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/middleware/test_request_id.py +0 -0
  228. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/middleware/test_request_logging.py +0 -0
  229. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/middleware/test_request_size_limit.py +0 -0
  230. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/middleware/test_security_headers.py +0 -0
  231. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/middleware/test_simple_domain_handler.py +0 -0
  232. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/middleware/test_throttle.py +0 -0
  233. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/use_case/__init__.py +0 -0
  234. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/use_case/test_protocols.py +0 -0
  235. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/validation/__init__.py +0 -0
  236. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/nene2/validation/test_exceptions.py +0 -0
  237. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/scripts/__init__.py +0 -0
  238. {nene2_python-1.8.8 → nene2_python-1.8.9}/tests/scripts/test_export_openapi.py +0 -0
@@ -5,6 +5,18 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
5
 
6
6
  ---
7
7
 
8
+ ## [1.8.9] — 2026-05-20
9
+
10
+ FT46〜FT49 フィールドトライアル — ドキュメント改善。
11
+
12
+ ### Added
13
+ - Field trial reports: `docs/field-trials/2026-05-field-trial-46.md` 〜 `docs/field-trials/2026-05-field-trial-49.md`
14
+
15
+ ### Changed
16
+ - `docs/how-to/run-tests.md` — SQLite 外部キー制約 (`PRAGMA foreign_keys=ON`) の注意事項を追記 (FT46)
17
+
18
+ ---
19
+
8
20
  ## [1.8.8] — 2026-05-20
9
21
 
10
22
  FT45 フィールドトライアル — SecurityHeadersMiddleware CSP バグ修正。
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nene2-python
3
- Version: 1.8.8
3
+ Version: 1.8.9
4
4
  Summary: NENE2 Python — minimal API framework following NENE2's design philosophy
5
5
  Project-URL: Homepage, https://github.com/hideyukiMORI/nene2-python
6
6
  Project-URL: Repository, https://github.com/hideyukiMORI/nene2-python
@@ -0,0 +1,128 @@
1
+ # Field Trial 46: DatabaseIntegrityException 実運用検証
2
+
3
+ **日付**: 2026-05-20
4
+ **バージョン**: v1.8.8 時点
5
+ **テーマ**: `DatabaseIntegrityException` を FastAPI エンドポイント経由で UNIQUE 制約・FK 制約違反を処理するパターンの実運用確認
6
+
7
+ ---
8
+
9
+ ## 概要
10
+
11
+ `SqlAlchemyQueryExecutor` を使って SQLite への UNIQUE/FK 制約違反を発生させ、
12
+ `DatabaseIntegrityException` を `DuplicateEmailError` / `InvalidUserReferenceError` に
13
+ サブクラス化してドメイン例外に変換し、`SimpleDomainHandler` で HTTP レスポンスにマッピングするパターンを実装した。
14
+
15
+ ---
16
+
17
+ ## 実装内容
18
+
19
+ `/home/xi/docker/nene2-python-FT/ft46-db-integrity/` に以下を作成:
20
+
21
+ - **`app.py`** — SQLite + `SqlAlchemyQueryExecutor` (Core API) を使ったユーザー/ポスト管理 API
22
+ - **`test_app.py`** — 正常系・UNIQUE 違反 409・FK 違反 422・エラー後回復 (7 件)
23
+ - **`test_friction.py`** — 摩擦点の確認テスト (4 件)
24
+
25
+ **テスト結果**: 11 件全通過 ✅
26
+
27
+ ---
28
+
29
+ ## 摩擦点
30
+
31
+ ### FP46-1: DatabaseIntegrityException のサブクラス化が必要
32
+
33
+ **分類**: 軽微な摩擦(パターン提示)
34
+
35
+ `DatabaseIntegrityException` は汎用例外のため、UNIQUE 制約違反(409)と FK 制約違反(422)を
36
+ 別の HTTP ステータスコードにマッピングするには、サブクラスを作って例外を変換する必要がある。
37
+
38
+ ```python
39
+ class DuplicateEmailError(DatabaseIntegrityException): pass
40
+ class InvalidUserReferenceError(DatabaseIntegrityException): pass
41
+
42
+ try:
43
+ executor.write("INSERT INTO users ...", params)
44
+ except DatabaseIntegrityException as exc:
45
+ raise DuplicateEmailError(str(exc)) from exc
46
+ ```
47
+
48
+ **判断**: データベース例外に対してドメイン固有の意味付けをするパターンとして自然。
49
+ `IntegrityError` のメッセージを解析して違反種別を判定する方法もあるが、
50
+ SQLite/MySQL/PostgreSQL でメッセージ形式が異なるため移植性に課題がある。
51
+ サブクラス + 明示的 raise パターンが最も移植性が高い。
52
+
53
+ ---
54
+
55
+ ### FP46-2: SQLite の FK 制約は PRAGMA foreign_keys=ON が必要
56
+
57
+ **分類**: 注意喚起(既知事項・ドキュメント追記価値あり)
58
+
59
+ SQLite のデフォルトでは外部キー制約が無効。
60
+ `create_engine()` 後に `PRAGMA foreign_keys=ON` を実行しないと、
61
+ FK 制約違反がスルーされて孤児レコードが挿入される。
62
+
63
+ ```python
64
+ with engine.begin() as conn:
65
+ conn.execute(text("PRAGMA foreign_keys=ON"))
66
+ ```
67
+
68
+ `StaticPool` を使う場合は最初の接続でこれを実行すれば全接続に適用される。
69
+
70
+ **判断**: SQLite 特有の注意点。`docs/how-to/run-tests.md` の StaticPool セクションに追記する価値がある。
71
+
72
+ ---
73
+
74
+ ### FP46-3: 整合性エラー後もトランザクションは自動ロールバックされる
75
+
76
+ **分類**: 摩擦なし(良い設計の確認)
77
+
78
+ `DatabaseIntegrityException` が発生すると `SqlAlchemyQueryExecutor.write()` 内で
79
+ `engine.begin()` のコンテキストマネージャーがロールバックする。
80
+ 次のリクエストでは新しいトランザクションが開始されるため、セッション汚染は発生しない。
81
+
82
+ **判断**: SQLAlchemy Core の設計通り。`engine.begin()` を使ったパターンは安全。
83
+
84
+ ---
85
+
86
+ ### FP46-4: SqlAlchemyQueryExecutor は SQL 文字列 API であり ORM Session ではない
87
+
88
+ **分類**: 摩擦あり(設計の理解不足・初回実装で失敗)
89
+
90
+ 当初 `SqlAlchemyQueryExecutor.write()` に ORM の `Session` を使ったコールバックを渡そうとしたが、
91
+ `write()` は SQL 文字列 + params を受け取る Core API であることを確認した。
92
+
93
+ ```python
94
+ # NG: コールバックパターン(TransactionManager のもの)
95
+ executor.write(lambda session: session.add(row)) # TypeError
96
+
97
+ # OK: SQL 文字列パターン(QueryExecutor のもの)
98
+ executor.write("INSERT INTO users (email) VALUES (:email)", {"email": "..."})
99
+ ```
100
+
101
+ ORM (Session) を使うパターンは `SqlAlchemyTransactionManager.transactional(callback)` で、
102
+ コールバック内では `_BoundQueryExecutor` を通じて SQL を実行する。
103
+ 直接 Session を使いたい場合はフレームワーク外で SQLAlchemy ORM を使う。
104
+
105
+ **判断**: フレームワークの設計通り(SQLAlchemy Core ベース)。
106
+ ドキュメントに Core API と ORM の違いを明記する価値がある。
107
+
108
+ ---
109
+
110
+ ## フレームワーク変更
111
+
112
+ なし(全て設計通りの挙動)
113
+
114
+ ドキュメント追記を検討:
115
+ - `docs/how-to/run-tests.md` の StaticPool セクションに SQLite FK pragma を追記
116
+ - `docs/how-to/` に `DatabaseIntegrityException` ハンドリングパターン how-to を追加
117
+
118
+ ---
119
+
120
+ ## 関連
121
+
122
+ - `nene2.database.DatabaseIntegrityException` (FT16, v1.7.0)
123
+ - `nene2.database.SqlAlchemyQueryExecutor`
124
+ - `nene2.database.SqlAlchemyTransactionManager`
125
+ - `nene2.middleware.SimpleDomainHandler` (FT21, v1.8.0)
126
+ - FT16 (DatabaseIntegrityException 実装, v1.7.0)
127
+ - FT17 (SqlAlchemyQueryExecutor.write() バグ修正, v1.7.0)
128
+ - FT34 (StaticPool SQLite テスト, v1.8.4)
@@ -0,0 +1,93 @@
1
+ # Field Trial 47: SqlAlchemyTransactionManager.transactional() 実運用検証
2
+
3
+ **日付**: 2026-05-20
4
+ **バージョン**: v1.8.8 時点
5
+ **テーマ**: `transactional()` コールバックパターンで複数テーブルへの同一トランザクション書き込みと、失敗時のロールバックを FastAPI エンドポイント経由で確認
6
+
7
+ ---
8
+
9
+ ## 概要
10
+
11
+ 銀行振込(送金元残高減算 + 送金先残高増加 + 転送ログ挿入の 3 操作)を
12
+ `SqlAlchemyTransactionManager.transactional()` の単一トランザクションで実装した。
13
+ 残高不足・存在しない口座での失敗時にすべての操作がロールバックされることを確認した。
14
+
15
+ ---
16
+
17
+ ## 実装内容
18
+
19
+ `/home/xi/docker/nene2-python-FT/ft47-transaction-manager/` に以下を作成:
20
+
21
+ - **`app.py`** — 口座管理 + 振込 API、`SqlAlchemyTransactionManager.transactional()` で複数書き込み
22
+ - **`test_app.py`** — 正常振込・残高更新・不足残高・存在しない口座・重複名 (7 件)
23
+ - **`test_friction.py`** — 摩擦点の確認テスト (4 件)
24
+
25
+ **テスト結果**: 11 件全通過 ✅
26
+
27
+ ---
28
+
29
+ ## 摩擦点
30
+
31
+ ### FP47-1: コールバックは DatabaseQueryExecutorInterface を受け取る
32
+
33
+ **分類**: 摩擦なし(良い設計の確認)
34
+
35
+ `transactional(callback)` のコールバックは `DatabaseQueryExecutorInterface` を引数として受け取る。
36
+ 内部的には `_BoundQueryExecutor` として同一接続にバインドされており、
37
+ `fetch_one()` / `write()` を呼ぶことで同一トランザクション内で複数操作できる。
38
+
39
+ **判断**: SQLAlchemy Core を抽象化したインターフェースが自然に機能する。
40
+
41
+ ---
42
+
43
+ ### FP47-2: ドメイン例外も transactional() を自動ロールバックさせる
44
+
45
+ **分類**: 摩擦なし(良い設計の確認)
46
+
47
+ コールバック内で `InsufficientFundsError` のような非 DB 例外を raise した場合、
48
+ `engine.begin()` コンテキストマネージャーが自動でロールバックしてから例外を伝播する。
49
+ `ErrorHandlerMiddleware` がこれを `SimpleDomainHandler` でキャッチして適切な HTTP レスポンスを返す。
50
+
51
+ **判断**: 設計通り。「例外発生 = ロールバック」が保証されているため、コールバック内で
52
+ ガード条件をチェックして素直に raise するだけで整合性が保たれる。
53
+
54
+ ---
55
+
56
+ ### FP47-3: 複数 write はすべて成功かすべてロールバックか(ACID 保証)
57
+
58
+ **分類**: 摩擦なし(良い設計の確認)
59
+
60
+ 送金処理の 3 つの write(送金元減算 / 送金先加算 / ログ挿入)は
61
+ `transactional()` が提供する `_BoundQueryExecutor` を通じて同一トランザクションで実行される。
62
+ 途中で例外が発生した場合、実行済みの write も含めてすべてロールバックされる。
63
+
64
+ **判断**: ACID の Atomicity が正しく動作することを確認した。
65
+
66
+ ---
67
+
68
+ ### FP47-4: transactional() の戻り値はコールバックの戻り値
69
+
70
+ **分類**: 摩擦なし(設計の確認)
71
+
72
+ `transactional()` はジェネリクス `[T]` を使ってコールバックの戻り値型を保持する。
73
+ コミット後の戻り値(`dict[str, object]` など)をそのまま受け取り、
74
+ FastAPI ハンドラーで `JSONResponse` に変換できる。
75
+
76
+ **判断**: 型安全なコールバックパターンが正しく動作する。
77
+
78
+ ---
79
+
80
+ ## フレームワーク変更
81
+
82
+ なし(全て設計通りの挙動)
83
+
84
+ ---
85
+
86
+ ## 関連
87
+
88
+ - `nene2.database.SqlAlchemyTransactionManager` (FT16, v1.7.0)
89
+ - `nene2.database.DatabaseQueryExecutorInterface`
90
+ - `nene2.database.DatabaseIntegrityException` (FT16, v1.7.0)
91
+ - FT16 (TransactionManager 実装, v1.7.0)
92
+ - FT38 (トランザクション管理確認, v1.8.5)
93
+ - FT46 (DatabaseIntegrityException 実運用, v1.8.8)
@@ -0,0 +1,96 @@
1
+ # Field Trial 48: CompositeHealthCheck + AsyncCompositeHealthCheck 実運用検証
2
+
3
+ **日付**: 2026-05-20
4
+ **バージョン**: v1.8.8 時点
5
+ **テーマ**: `CompositeHealthCheck` と `AsyncCompositeHealthCheck` を `/health` エンドポイントで使い、複数コンポーネントのヘルスチェックを集約するパターンの実運用確認
6
+
7
+ ---
8
+
9
+ ## 概要
10
+
11
+ 同期 `CompositeHealthCheck`(DB + キャッシュ)と非同期 `AsyncCompositeHealthCheck`(DB + 外部 API)を
12
+ `/health` と `/health/async` エンドポイントで使い、部分的な失敗時の 503 レスポンスと
13
+ `checks` フィールドへの詳細情報付与を確認した。
14
+
15
+ ---
16
+
17
+ ## 実装内容
18
+
19
+ `/home/xi/docker/nene2-python-FT/ft48-health-check/` に以下を作成:
20
+
21
+ - **`app.py`** — `CompositeHealthCheck` + `AsyncCompositeHealthCheck` を使った FastAPI アプリ
22
+ - **`test_app.py`** — 正常・503・checks フィールド・エラーメッセージ確認 (7 件)
23
+ - **`test_friction.py`** — 摩擦点の確認テスト (4 件)
24
+
25
+ **テスト結果**: 11 件全通過 ✅
26
+
27
+ ---
28
+
29
+ ## 摩擦点
30
+
31
+ ### FP48-1: http_status_code プロパティで 200/503 が自動判定される
32
+
33
+ **分類**: 摩擦なし(良い設計の確認)
34
+
35
+ FT31 で追加した `HealthStatus.http_status_code` プロパティが
36
+ `CompositeHealthCheck` の結果でも正しく機能する。
37
+ `is_healthy` が `True` なら 200、`False` なら 503 を返す。
38
+ ハンドラーで `status_code=status.http_status_code` と書くだけで適切なステータスが返る。
39
+
40
+ ---
41
+
42
+ ### FP48-2: 空のチェックリストは "ok" を返す
43
+
44
+ **分類**: 摩擦なし(エッジケース確認)
45
+
46
+ `CompositeHealthCheck([])` のように空リストを渡すと、
47
+ 全チェック通過として `HealthStatus(status="ok", checks={})` を返す。
48
+ ゼロ個のチェックは「失敗するチェックがない」として ok と見なす設計は直感的。
49
+
50
+ ---
51
+
52
+ ### FP48-3: 部分的な失敗は全コンポーネントの results を含む
53
+
54
+ **分類**: 摩擦なし(良い設計の確認)
55
+
56
+ 失敗したコンポーネントのエラーメッセージと成功したコンポーネントの "ok" が
57
+ `checks` フィールドに混在する。
58
+ クライアントはどのコンポーネントが失敗したかを `checks` フィールドで判別できる。
59
+
60
+ ```json
61
+ {
62
+ "status": "error",
63
+ "checks": {
64
+ "database": "ok",
65
+ "cache": "connection refused"
66
+ }
67
+ }
68
+ ```
69
+
70
+ ---
71
+
72
+ ### FP48-4: AsyncCompositeHealthCheck は TestClient でも並列実行される
73
+
74
+ **分類**: 摩擦なし(良い設計の確認)
75
+
76
+ `AsyncCompositeHealthCheck` は `asyncio.gather()` で並列実行するため、
77
+ TestClient(同期コンテキスト)経由でもエンドポイント内部では asyncio が使われ、
78
+ 並列性が維持される。
79
+ 50ms の遅延を持つチェックを含む場合でも、逐次実行の 2 倍未満の時間で完了する。
80
+
81
+ ---
82
+
83
+ ## フレームワーク変更
84
+
85
+ なし(全て設計通りの挙動)
86
+
87
+ ---
88
+
89
+ ## 関連
90
+
91
+ - `nene2.http.CompositeHealthCheck` (FT22, v1.8.0)
92
+ - `nene2.http.AsyncCompositeHealthCheck` (FT36, v1.8.4)
93
+ - `nene2.http.HealthStatus.http_status_code` (FT31, v1.8.3)
94
+ - FT22 (CompositeHealthCheck 実装, v1.8.0)
95
+ - FT31 (http_status_code プロパティ追加, v1.8.3)
96
+ - FT36 (AsyncCompositeHealthCheck 実装, v1.8.4)
@@ -0,0 +1,96 @@
1
+ # Field Trial 49: AppSettings 実運用検証
2
+
3
+ **日付**: 2026-05-20
4
+ **バージョン**: v1.8.8 時点
5
+ **テーマ**: `AppSettings` の環境変数オーバーライド・バリデーション・`db_url` プロパティ・SecretStr の動作を確認
6
+
7
+ ---
8
+
9
+ ## 概要
10
+
11
+ `AppSettings` の全主要機能(環境変数オーバーライド・バリデーター・`db_url` プロパティ生成・
12
+ SecretStr によるパスワード保護・リスト型環境変数の設定方法)を確認した。
13
+
14
+ ---
15
+
16
+ ## 実装内容
17
+
18
+ `/home/xi/docker/nene2-python-FT/ft49-app-settings/` に以下を作成:
19
+
20
+ - **`test_settings.py`** — AppSettings の全機能確認 (13 件)
21
+
22
+ **テスト結果**: 13 件全通過 ✅
23
+
24
+ ---
25
+
26
+ ## 摩擦点
27
+
28
+ ### FP49-1: db_password は SecretStr のため repr に平文が出ない
29
+
30
+ **分類**: 摩擦なし(良い設計の確認)
31
+
32
+ `db_password: SecretStr` のため、`repr(settings)` や `print(settings)` で
33
+ パスワードが平文で出力されない。ログに誤ってパスワードが漏洩しない設計。
34
+
35
+ 実際の値が必要な場合は `settings.db_password.get_secret_value()` を使う。
36
+ `db_url` プロパティが内部で呼んでいるので、通常は直接呼ぶ必要はない。
37
+
38
+ **判断**: セキュリティ上の重要な設計。
39
+
40
+ ---
41
+
42
+ ### FP49-2: リスト型環境変数は JSON 配列形式で設定する
43
+
44
+ **分類**: 軽微な摩擦(注意喚起)
45
+
46
+ `bearer_tokens`・`api_keys`・`cors_origins` などのリスト型フィールドを
47
+ 環境変数で設定する場合、pydantic-settings は JSON 配列形式を期待する:
48
+
49
+ ```bash
50
+ # OK: JSON 配列形式
51
+ export BEARER_TOKENS='["token-a", "token-b"]'
52
+
53
+ # NG: カンマ区切り(動作しない)
54
+ export BEARER_TOKENS="token-a,token-b"
55
+ ```
56
+
57
+ `LocalTokenVerifier.from_env()` (FT11) はカンマ区切りをサポートしているが、
58
+ `AppSettings.bearer_tokens` 自体は JSON 配列形式が必要。
59
+
60
+ **判断**: pydantic-settings の仕様通り。NENE2 の `README` や設定リファレンスドキュメントに
61
+ 明記する価値がある。
62
+
63
+ ---
64
+
65
+ ### FP49-3: app_env は "local" / "test" / "production" のみ
66
+
67
+ **分類**: 摩擦なし(設計の確認)
68
+
69
+ `validate_app_env` バリデーターで "staging" など他の値を拒否する。
70
+ デプロイ環境を厳密に 3 種類に限定することで設定ミスを防ぐ。
71
+
72
+ ---
73
+
74
+ ### FP49-4: log_level は大文字に自動正規化される
75
+
76
+ **分類**: 摩擦なし(良い設計の確認)
77
+
78
+ `validate_log_level` バリデーターで "debug" → "DEBUG" に変換される。
79
+ 大文字・小文字を気にせずに設定できる。
80
+
81
+ ---
82
+
83
+ ## フレームワーク変更
84
+
85
+ なし(全て設計通りの挙動)
86
+
87
+ ドキュメント追記を検討:
88
+ - `docs/reference/configuration.md` にリスト型環境変数の設定方法を明記
89
+
90
+ ---
91
+
92
+ ## 関連
93
+
94
+ - `nene2.config.AppSettings`
95
+ - FT11 (LocalTokenVerifier.from_env, v1.4.0)
96
+ - FT26 (setup_logging log_level パラメータ, v1.8.1)
@@ -103,6 +103,17 @@ engine = create_engine(
103
103
 
104
104
  `StaticPool` guarantees all logical connections share the same underlying SQLite connection, so tables created in one operation are visible to the next.
105
105
 
106
+ **SQLite foreign-key enforcement**: SQLite disables foreign-key constraints by default. Enable them with `PRAGMA foreign_keys=ON` right after the engine is created:
107
+
108
+ ```python
109
+ from sqlalchemy import text
110
+
111
+ with engine.begin() as conn:
112
+ conn.execute(text("PRAGMA foreign_keys=ON"))
113
+ ```
114
+
115
+ With `StaticPool`, one call applies to the single shared connection, so all subsequent operations see FK constraints enforced.
116
+
106
117
  ## Capturing structlog output with caplog
107
118
 
108
119
  Call `configure_for_testing()` at module level in `conftest.py` to route structlog through stdlib logging so pytest's `caplog` fixture can capture it.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nene2-python"
3
- version = "1.8.8"
3
+ version = "1.8.9"
4
4
  description = "NENE2 Python — minimal API framework following NENE2's design philosophy"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}
@@ -925,7 +925,7 @@ wheels = [
925
925
 
926
926
  [[package]]
927
927
  name = "nene2-python"
928
- version = "1.8.8"
928
+ version = "1.8.9"
929
929
  source = { editable = "." }
930
930
  dependencies = [
931
931
  { name = "alembic" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes