nene2-python 1.8.36__tar.gz → 1.8.38__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 (388) hide show
  1. {nene2_python-1.8.36 → nene2_python-1.8.38}/PKG-INFO +1 -1
  2. nene2_python-1.8.38/docs/field-trials/2026-05-field-trial-166.md +246 -0
  3. nene2_python-1.8.38/docs/field-trials/2026-05-field-trial-167.md +239 -0
  4. {nene2_python-1.8.36 → nene2_python-1.8.38}/pyproject.toml +1 -1
  5. {nene2_python-1.8.36 → nene2_python-1.8.38}/.env.example +0 -0
  6. {nene2_python-1.8.36 → nene2_python-1.8.38}/.github/workflows/ci.yml +0 -0
  7. {nene2_python-1.8.36 → nene2_python-1.8.38}/.github/workflows/docs.yml +0 -0
  8. {nene2_python-1.8.36 → nene2_python-1.8.38}/.github/workflows/publish.yml +0 -0
  9. {nene2_python-1.8.36 → nene2_python-1.8.38}/.gitignore +0 -0
  10. {nene2_python-1.8.36 → nene2_python-1.8.38}/.vitepress/config.mts +0 -0
  11. {nene2_python-1.8.36 → nene2_python-1.8.38}/.vitepress/theme/custom.css +0 -0
  12. {nene2_python-1.8.36 → nene2_python-1.8.38}/.vitepress/theme/index.ts +0 -0
  13. {nene2_python-1.8.36 → nene2_python-1.8.38}/AGENTS.md +0 -0
  14. {nene2_python-1.8.36 → nene2_python-1.8.38}/CHANGELOG.md +0 -0
  15. {nene2_python-1.8.36 → nene2_python-1.8.38}/CLAUDE.md +0 -0
  16. {nene2_python-1.8.36 → nene2_python-1.8.38}/Dockerfile +0 -0
  17. {nene2_python-1.8.36 → nene2_python-1.8.38}/LICENSE +0 -0
  18. {nene2_python-1.8.36 → nene2_python-1.8.38}/README.md +0 -0
  19. {nene2_python-1.8.36 → nene2_python-1.8.38}/alembic/README +0 -0
  20. {nene2_python-1.8.36 → nene2_python-1.8.38}/alembic/env.py +0 -0
  21. {nene2_python-1.8.36 → nene2_python-1.8.38}/alembic/script.py.mako +0 -0
  22. {nene2_python-1.8.36 → nene2_python-1.8.38}/alembic/versions/001_create_notes_and_tags_tables.py +0 -0
  23. {nene2_python-1.8.36 → nene2_python-1.8.38}/alembic.ini +0 -0
  24. {nene2_python-1.8.36 → nene2_python-1.8.38}/compose.yaml +0 -0
  25. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/adr/0001-toolchain.md +0 -0
  26. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/adr/0002-clean-architecture.md +0 -0
  27. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/adr/0003-security-first.md +0 -0
  28. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/adr/0004-ai-first-design.md +0 -0
  29. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/adr/0005-logging.md +0 -0
  30. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/adr/0006-rate-limiting.md +0 -0
  31. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/adr/0009-mcp-design.md +0 -0
  32. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/adr/0010-async-use-case.md +0 -0
  33. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/adr/0011-mcp-as-core-dependency.md +0 -0
  34. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/de/index.md +0 -0
  35. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/de/tutorials/getting-started.md +0 -0
  36. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/explanation/architecture.md +0 -0
  37. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/explanation/design-philosophy.md +0 -0
  38. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-1.md +0 -0
  39. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-10.md +0 -0
  40. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-100.md +0 -0
  41. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-101.md +0 -0
  42. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-102.md +0 -0
  43. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-103.md +0 -0
  44. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-104.md +0 -0
  45. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-105.md +0 -0
  46. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-106.md +0 -0
  47. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-107.md +0 -0
  48. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-108.md +0 -0
  49. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-109.md +0 -0
  50. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-11.md +0 -0
  51. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-110.md +0 -0
  52. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-111.md +0 -0
  53. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-112.md +0 -0
  54. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-113.md +0 -0
  55. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-114.md +0 -0
  56. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-115.md +0 -0
  57. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-116.md +0 -0
  58. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-117.md +0 -0
  59. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-118.md +0 -0
  60. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-119.md +0 -0
  61. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-12.md +0 -0
  62. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-120.md +0 -0
  63. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-121.md +0 -0
  64. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-122.md +0 -0
  65. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-123.md +0 -0
  66. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-124.md +0 -0
  67. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-125.md +0 -0
  68. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-126.md +0 -0
  69. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-127.md +0 -0
  70. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-128.md +0 -0
  71. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-129.md +0 -0
  72. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-13.md +0 -0
  73. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-130.md +0 -0
  74. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-131.md +0 -0
  75. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-132.md +0 -0
  76. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-133.md +0 -0
  77. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-134.md +0 -0
  78. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-135.md +0 -0
  79. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-136.md +0 -0
  80. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-137.md +0 -0
  81. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-138.md +0 -0
  82. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-139.md +0 -0
  83. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-14.md +0 -0
  84. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-140.md +0 -0
  85. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-141.md +0 -0
  86. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-142.md +0 -0
  87. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-143.md +0 -0
  88. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-144.md +0 -0
  89. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-145.md +0 -0
  90. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-146.md +0 -0
  91. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-147.md +0 -0
  92. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-148.md +0 -0
  93. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-149.md +0 -0
  94. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-15.md +0 -0
  95. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-150.md +0 -0
  96. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-151.md +0 -0
  97. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-152.md +0 -0
  98. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-153.md +0 -0
  99. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-154.md +0 -0
  100. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-155.md +0 -0
  101. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-156.md +0 -0
  102. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-157.md +0 -0
  103. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-158.md +0 -0
  104. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-159.md +0 -0
  105. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-16.md +0 -0
  106. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-160.md +0 -0
  107. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-161.md +0 -0
  108. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-162.md +0 -0
  109. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-163.md +0 -0
  110. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-164.md +0 -0
  111. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-165.md +0 -0
  112. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-17.md +0 -0
  113. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-18.md +0 -0
  114. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-19.md +0 -0
  115. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-2.md +0 -0
  116. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-20.md +0 -0
  117. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-21.md +0 -0
  118. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-22.md +0 -0
  119. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-23.md +0 -0
  120. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-24.md +0 -0
  121. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-25.md +0 -0
  122. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-26.md +0 -0
  123. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-27.md +0 -0
  124. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-28.md +0 -0
  125. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-29.md +0 -0
  126. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-3.md +0 -0
  127. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-30.md +0 -0
  128. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-31.md +0 -0
  129. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-32.md +0 -0
  130. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-33.md +0 -0
  131. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-34.md +0 -0
  132. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-35.md +0 -0
  133. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-36.md +0 -0
  134. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-37.md +0 -0
  135. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-38.md +0 -0
  136. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-39.md +0 -0
  137. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-4.md +0 -0
  138. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-40.md +0 -0
  139. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-41.md +0 -0
  140. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-42.md +0 -0
  141. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-43.md +0 -0
  142. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-44.md +0 -0
  143. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-45.md +0 -0
  144. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-46.md +0 -0
  145. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-47.md +0 -0
  146. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-48.md +0 -0
  147. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-49.md +0 -0
  148. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-5.md +0 -0
  149. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-50.md +0 -0
  150. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-51.md +0 -0
  151. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-52.md +0 -0
  152. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-53.md +0 -0
  153. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-54.md +0 -0
  154. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-55.md +0 -0
  155. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-56.md +0 -0
  156. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-57.md +0 -0
  157. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-58.md +0 -0
  158. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-59.md +0 -0
  159. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-6.md +0 -0
  160. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-60.md +0 -0
  161. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-61.md +0 -0
  162. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-62.md +0 -0
  163. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-63.md +0 -0
  164. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-64.md +0 -0
  165. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-65.md +0 -0
  166. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-66.md +0 -0
  167. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-67.md +0 -0
  168. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-68.md +0 -0
  169. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-69.md +0 -0
  170. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-7.md +0 -0
  171. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-70.md +0 -0
  172. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-71.md +0 -0
  173. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-72.md +0 -0
  174. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-73.md +0 -0
  175. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-74.md +0 -0
  176. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-75.md +0 -0
  177. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-76.md +0 -0
  178. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-77.md +0 -0
  179. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-78.md +0 -0
  180. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-79.md +0 -0
  181. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-8.md +0 -0
  182. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-80.md +0 -0
  183. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-81.md +0 -0
  184. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-82.md +0 -0
  185. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-83.md +0 -0
  186. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-84.md +0 -0
  187. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-85.md +0 -0
  188. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-86.md +0 -0
  189. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-87.md +0 -0
  190. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-88.md +0 -0
  191. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-89.md +0 -0
  192. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-9.md +0 -0
  193. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-90.md +0 -0
  194. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-91.md +0 -0
  195. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-92.md +0 -0
  196. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-93.md +0 -0
  197. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-94.md +0 -0
  198. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-95.md +0 -0
  199. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-96.md +0 -0
  200. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-97.md +0 -0
  201. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-98.md +0 -0
  202. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/field-trials/2026-05-field-trial-99.md +0 -0
  203. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/fr/index.md +0 -0
  204. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/fr/tutorials/getting-started.md +0 -0
  205. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/how-to/add-new-domain.md +0 -0
  206. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/how-to/api-versioning.md +0 -0
  207. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/how-to/async-use-case.md +0 -0
  208. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/how-to/background-tasks.md +0 -0
  209. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/how-to/configure-auth.md +0 -0
  210. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/how-to/cors.md +0 -0
  211. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/how-to/custom-auth-middleware.md +0 -0
  212. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/how-to/dependency-injection.md +0 -0
  213. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/how-to/domain-events.md +0 -0
  214. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/how-to/file-upload.md +0 -0
  215. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/how-to/lifespan-and-app-state.md +0 -0
  216. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/how-to/middleware-stack.md +0 -0
  217. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/how-to/new-project.md +0 -0
  218. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/how-to/problem-details.md +0 -0
  219. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/how-to/response-patterns.md +0 -0
  220. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/how-to/run-tests.md +0 -0
  221. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/how-to/soft-delete.md +0 -0
  222. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/how-to/sqlalchemy-repository.md +0 -0
  223. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/how-to/streaming.md +0 -0
  224. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/how-to/structured-logging.md +0 -0
  225. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/how-to/validation.md +0 -0
  226. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/how-to/webhook.md +0 -0
  227. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/howto/mcp-setup.md +0 -0
  228. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/index.md +0 -0
  229. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/ja/explanation/architecture.md +0 -0
  230. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/ja/explanation/design-philosophy.md +0 -0
  231. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/ja/how-to/add-new-domain.md +0 -0
  232. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/ja/how-to/configure-auth.md +0 -0
  233. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/ja/how-to/new-project.md +0 -0
  234. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/ja/how-to/run-tests.md +0 -0
  235. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/ja/how-to/sqlalchemy-repository.md +0 -0
  236. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/ja/howto/mcp-setup.md +0 -0
  237. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/ja/index.md +0 -0
  238. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/ja/reference/api.md +0 -0
  239. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/ja/reference/configuration.md +0 -0
  240. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/ja/reference/framework-modules.md +0 -0
  241. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/ja/tutorials/first-domain.md +0 -0
  242. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/ja/tutorials/getting-started.md +0 -0
  243. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/pt-br/index.md +0 -0
  244. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/pt-br/tutorials/getting-started.md +0 -0
  245. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/reference/api.md +0 -0
  246. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/reference/configuration.md +0 -0
  247. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/reference/framework-modules.md +0 -0
  248. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/roadmap.md +0 -0
  249. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/templates/field-trial-report.md +0 -0
  250. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/todo/current.md +0 -0
  251. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/tutorials/first-domain.md +0 -0
  252. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/tutorials/getting-started.md +0 -0
  253. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/zh/index.md +0 -0
  254. {nene2_python-1.8.36 → nene2_python-1.8.38}/docs/zh/tutorials/getting-started.md +0 -0
  255. {nene2_python-1.8.36 → nene2_python-1.8.38}/package-lock.json +0 -0
  256. {nene2_python-1.8.36 → nene2_python-1.8.38}/package.json +0 -0
  257. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/__init__.py +0 -0
  258. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/__main__.py +0 -0
  259. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/app.py +0 -0
  260. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/comment/__init__.py +0 -0
  261. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/comment/entity.py +0 -0
  262. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/comment/exceptions.py +0 -0
  263. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/comment/handler.py +0 -0
  264. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/comment/repository.py +0 -0
  265. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/comment/sqlalchemy_repository.py +0 -0
  266. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/comment/use_case.py +0 -0
  267. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/mcp.py +0 -0
  268. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/note/__init__.py +0 -0
  269. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/note/async_use_case.py +0 -0
  270. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/note/entity.py +0 -0
  271. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/note/exceptions.py +0 -0
  272. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/note/handler.py +0 -0
  273. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/note/repository.py +0 -0
  274. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/note/sqlalchemy_repository.py +0 -0
  275. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/note/use_case.py +0 -0
  276. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/schema.py +0 -0
  277. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/tag/__init__.py +0 -0
  278. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/tag/entity.py +0 -0
  279. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/tag/exceptions.py +0 -0
  280. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/tag/handler.py +0 -0
  281. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/tag/repository.py +0 -0
  282. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/tag/sqlalchemy_repository.py +0 -0
  283. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/example/tag/use_case.py +0 -0
  284. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/__init__.py +0 -0
  285. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/auth/__init__.py +0 -0
  286. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/auth/api_key.py +0 -0
  287. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/auth/bearer_token.py +0 -0
  288. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/auth/deps.py +0 -0
  289. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/auth/exceptions.py +0 -0
  290. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/auth/interfaces.py +0 -0
  291. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/auth/local_verifier.py +0 -0
  292. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/cache/__init__.py +0 -0
  293. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/cache/ttl.py +0 -0
  294. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/config/__init__.py +0 -0
  295. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/config/settings.py +0 -0
  296. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/database/__init__.py +0 -0
  297. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/database/exceptions.py +0 -0
  298. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/database/health.py +0 -0
  299. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/database/interfaces.py +0 -0
  300. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/database/sqlalchemy_executor.py +0 -0
  301. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/database/utils.py +0 -0
  302. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/http/__init__.py +0 -0
  303. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/http/etag.py +0 -0
  304. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/http/health.py +0 -0
  305. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/http/pagination.py +0 -0
  306. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/http/problem_details.py +0 -0
  307. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/log/__init__.py +0 -0
  308. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/log/setup.py +0 -0
  309. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/mcp/__init__.py +0 -0
  310. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/mcp/http_client.py +0 -0
  311. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/mcp/server.py +0 -0
  312. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/middleware/__init__.py +0 -0
  313. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/middleware/domain_exception.py +0 -0
  314. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/middleware/error_handler.py +0 -0
  315. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/middleware/request_id.py +0 -0
  316. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/middleware/request_logging.py +0 -0
  317. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/middleware/request_size_limit.py +0 -0
  318. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/middleware/security_headers.py +0 -0
  319. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/middleware/setup.py +0 -0
  320. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/middleware/throttle.py +0 -0
  321. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/py.typed +0 -0
  322. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/security/__init__.py +0 -0
  323. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/security/webhook.py +0 -0
  324. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/use_case/__init__.py +0 -0
  325. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/use_case/protocols.py +0 -0
  326. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/validation/__init__.py +0 -0
  327. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/nene2/validation/exceptions.py +0 -0
  328. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/scripts/__init__.py +0 -0
  329. {nene2_python-1.8.36 → nene2_python-1.8.38}/src/scripts/export_openapi.py +0 -0
  330. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/__init__.py +0 -0
  331. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/conftest.py +0 -0
  332. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/example/__init__.py +0 -0
  333. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/example/comment/__init__.py +0 -0
  334. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/example/comment/test_comment_http.py +0 -0
  335. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/example/comment/test_comment_repository.py +0 -0
  336. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/example/comment/test_comment_use_case.py +0 -0
  337. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/example/conftest.py +0 -0
  338. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/example/note/__init__.py +0 -0
  339. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/example/note/test_async_note_use_case.py +0 -0
  340. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/example/note/test_list_notes.py +0 -0
  341. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/example/note/test_note_repository.py +0 -0
  342. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/example/tag/__init__.py +0 -0
  343. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/example/tag/test_tag_repository.py +0 -0
  344. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/example/tag/test_tags.py +0 -0
  345. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/example/test_cors.py +0 -0
  346. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/example/test_mcp.py +0 -0
  347. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/__init__.py +0 -0
  348. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/auth/__init__.py +0 -0
  349. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/auth/test_api_key.py +0 -0
  350. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/auth/test_bearer_token.py +0 -0
  351. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/auth/test_make_require_auth.py +0 -0
  352. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/auth/test_token_issuer.py +0 -0
  353. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/cache/__init__.py +0 -0
  354. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/cache/test_ttl.py +0 -0
  355. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/config/__init__.py +0 -0
  356. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/config/test_settings.py +0 -0
  357. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/database/__init__.py +0 -0
  358. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/database/test_transaction.py +0 -0
  359. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/database/test_utils.py +0 -0
  360. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/http/__init__.py +0 -0
  361. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/http/test_etag.py +0 -0
  362. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/http/test_health.py +0 -0
  363. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/http/test_pagination.py +0 -0
  364. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/http/test_problem_details.py +0 -0
  365. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/log/__init__.py +0 -0
  366. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/log/test_setup.py +0 -0
  367. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/mcp/__init__.py +0 -0
  368. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/mcp/test_http_client.py +0 -0
  369. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/mcp/test_server.py +0 -0
  370. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/middleware/__init__.py +0 -0
  371. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/middleware/test_error_handler.py +0 -0
  372. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/middleware/test_request_id.py +0 -0
  373. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/middleware/test_request_logging.py +0 -0
  374. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/middleware/test_request_size_limit.py +0 -0
  375. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/middleware/test_security_headers.py +0 -0
  376. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/middleware/test_setup_middlewares.py +0 -0
  377. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/middleware/test_simple_domain_handler.py +0 -0
  378. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/middleware/test_throttle.py +0 -0
  379. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/security/__init__.py +0 -0
  380. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/security/test_webhook.py +0 -0
  381. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/use_case/__init__.py +0 -0
  382. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/use_case/test_protocols.py +0 -0
  383. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/use_case/test_run_in_threadpool.py +0 -0
  384. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/validation/__init__.py +0 -0
  385. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/nene2/validation/test_exceptions.py +0 -0
  386. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/scripts/__init__.py +0 -0
  387. {nene2_python-1.8.36 → nene2_python-1.8.38}/tests/scripts/test_export_openapi.py +0 -0
  388. {nene2_python-1.8.36 → nene2_python-1.8.38}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nene2-python
3
- Version: 1.8.36
3
+ Version: 1.8.38
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,246 @@
1
+ # FT166: functools モジュール
2
+
3
+ **日付**: 2026-05-21
4
+ **テーマ**: `functools` モジュール — `lru_cache`・`cached_property`・`partial`・`wraps`・`reduce`・`cache`
5
+ **セキュリティ診断**: なし(166 % 3 = 1)
6
+
7
+ ---
8
+
9
+ ## 概要
10
+
11
+ Python 標準ライブラリの `functools` モジュールを nene2-python フレームワーク上で検証した。
12
+ `functools` は高階関数・デコレーター・メモ化のユーティリティを提供し、
13
+ nene2-python の DI パターン・レスポンスキャッシュ・ミドルウェア実装に直結する。
14
+
15
+ ---
16
+
17
+ ## 実装したサンプルアプリ
18
+
19
+ **場所**: `/home/xi/docker/nene2-python-FT/ft166-functools/`
20
+
21
+ ### 主要機能
22
+
23
+ | 関数/クラス | 概要 |
24
+ |---|---|
25
+ | `fibonacci(n)` | `@lru_cache(maxsize=128)` でメモ化された再帰 fibonacci |
26
+ | `expensive_computation(key)` | `@lru_cache(maxsize=32)` で文字列キーのキャッシュ |
27
+ | `HeavyComputer` | `@cached_property` で total / average を遅延計算・キャッシュ |
28
+ | `double / triple` | `functools.partial` で multiply の引数を固定 |
29
+ | `greet_hello / greet_hi` | `partial` でテンプレートと記号を固定したあいさつ生成 |
30
+ | `timing_decorator` | `@functools.wraps` でデコレーターが元の関数名・docstring を保持 |
31
+ | `product_reduce(numbers)` | `functools.reduce` で積を計算 |
32
+ | `flatten_reduce(nested)` | `reduce` でネストしたリストをフラット化 |
33
+ | `collatz_steps(n)` | `@functools.cache`(unbounded)でコラッツ数列ステップ数をメモ化 |
34
+
35
+ ### HTTP エンドポイント
36
+
37
+ | メソッド | パス | 概要 |
38
+ |---|---|---|
39
+ | GET | `/functools/fibonacci` | lru_cache 付き fibonacci(キャッシュ統計返却) |
40
+ | GET | `/functools/lru-cache` | 任意キーのキャッシュ統計デモ |
41
+ | GET | `/functools/cached-property` | HeavyComputer の計算回数デモ |
42
+ | GET | `/functools/partial` | double / triple / greet デモ |
43
+ | GET | `/functools/wraps` | slow_add の __name__ 保持確認 |
44
+ | POST | `/functools/reduce-product` | 整数リストの積 |
45
+ | POST | `/functools/reduce-flatten` | ネストリストのフラット化 |
46
+ | GET | `/functools/cache-collatz` | unbounded cache でコラッツ数列 |
47
+
48
+ ---
49
+
50
+ ## テスト結果
51
+
52
+ **34 passed(摩擦ゼロ)**
53
+
54
+ ```
55
+ 34 passed in 0.83s
56
+ ```
57
+
58
+ ---
59
+
60
+ ## 摩擦ポイント
61
+
62
+ **今回の FT では実装上の摩擦はゼロだった。**
63
+
64
+ ---
65
+
66
+ ## 観察点
67
+
68
+ ### 観察1: `@lru_cache` はグローバル状態 — テスト間でキャッシュを `clear` する必要がある
69
+
70
+ ```python
71
+ @pytest.fixture(autouse=True)
72
+ def clear_caches() -> None:
73
+ fibonacci.cache_clear()
74
+ expensive_computation.cache_clear()
75
+ ```
76
+
77
+ `@lru_cache` の対象関数はプロセス共有のキャッシュを持つ。
78
+ テスト間でキャッシュが汚染されるため `autouse=True` で毎回クリアする必要がある。
79
+ nene2-python でキャッシュを使う UseCase は `cache_clear()` を lifespan の shutdown で呼ぶか、
80
+ または `@lru_cache` をクラスメソッドに適用してインスタンス単位でスコープを制御する。
81
+
82
+ ### 観察2: `@cached_property` はインスタンス単位 — `lru_cache` との使い分け
83
+
84
+ ```python
85
+ class HeavyComputer:
86
+ @functools.cached_property
87
+ def total(self) -> int:
88
+ self.compute_count += 1
89
+ return sum(self._data)
90
+ ```
91
+
92
+ `@cached_property` はインスタンスの `__dict__` に値を保存する。
93
+ 同じインスタンスで 2 回目のアクセスは計算なし。新しいインスタンスは再計算。
94
+ `@lru_cache` はプロセス全体で共有・引数でキー管理、`@cached_property` はインスタンスで管理。
95
+ nene2-python での使い分け:
96
+ - UseCase / Repository インスタンスの設定値計算 → `@cached_property`
97
+ - 引数ベースの重い計算(ページネーション計算など)→ `@lru_cache`
98
+
99
+ ### 観察3: `@functools.cache` = `@lru_cache(maxsize=None)` でメモリ無制限
100
+
101
+ ```python
102
+ @functools.cache # Python 3.9+ / unbounded
103
+ def collatz_steps(n: int) -> int: ...
104
+ ```
105
+
106
+ `@cache` は `@lru_cache(maxsize=None)` の略記で、キャッシュサイズ制限なし。
107
+ 再帰的な数学関数に適しているが、引数の値域が大きい場合はメモリ枯渇に注意。
108
+ nene2-python での用途: 設定値の解析結果など、引数の種類が少ない場合に使う。
109
+
110
+ ### 観察4: `functools.partial` で DI 設定をカリー化できる
111
+
112
+ ```python
113
+ double = functools.partial(multiply, y=2)
114
+ greet_hello = functools.partial(make_greeting, "Hello")
115
+ ```
116
+
117
+ FastAPI の `Depends` と組み合わせる場合、`partial` でリポジトリに設定を注入できる:
118
+ ```python
119
+ get_repo = functools.partial(NoteRepository, db_url=settings.db_url)
120
+ Depends(get_repo)
121
+ ```
122
+ ただし `Depends` に渡す関数はシグネチャが重要なため、`partial` が残す引数を確認すること。
123
+
124
+ ### 観察5: `@functools.wraps` なしのデコレーターは `__name__` を上書きする
125
+
126
+ ```python
127
+ def timing_decorator(func):
128
+ # @functools.wraps(func) がないと:
129
+ def wrapper(*args, **kwargs): ...
130
+ return wrapper # → slow_add.__name__ == "wrapper" になる
131
+
132
+ def timing_decorator(func):
133
+ @functools.wraps(func) # ← これがないと OpenAPI ルート名が壊れる
134
+ def wrapper(*args, **kwargs): ...
135
+ return wrapper
136
+ ```
137
+
138
+ FastAPI はルート関数の `__name__` を operationId に使う。
139
+ カスタムミドルウェア・デコレーターに `@functools.wraps` がないと OpenAPI スキーマが壊れる。
140
+
141
+ ---
142
+
143
+ ## nene2-python フレームワークとの統合
144
+
145
+ - `@lru_cache` は `TtlCache`(FT100 で追加済み)と役割が異なる(TTL なし vs TTL あり)
146
+ - `@cached_property` は Repository の設定パース結果のキャッシュに最適
147
+ - `@functools.wraps` は nene2-python の `BearerTokenMiddleware` 等のデコレーターで使用が推奨される
148
+ - `functools.reduce` は UseCase の集計ロジックに使えるが、`sum()` / リスト内包表記の方が可読性が高い場合が多い
149
+ - `@functools.cache` は設定値の解析・静的ルックアップに適している
150
+
151
+ ---
152
+
153
+ ## Developer Experience (DX) Review
154
+
155
+ ### ペルソナ1: 初心者(Python 歴1年・独学中・女性・バックエンド志望)
156
+
157
+ `@lru_cache` は「デコレーターをつけるとキャッシュされる」という概念は分かりやすいが、
158
+ `cache_clear()` が必要なことや、グローバル共有であることは見落としやすい。
159
+
160
+ **ドキュメント理解**: `@lru_cache` のサンプルはフィボナッチが定番だが、
161
+ 「Web API の UseCase でいつ使うか」の例がないと「難しそうなやつ」で止まる。
162
+
163
+ **事故リスク**: 中。`@lru_cache` をテストで使うときにキャッシュをクリアし忘れ、
164
+ 他のテストのキャッシュが漏れ込んでテストが通ったり失敗したりするフレーキーなテストが生まれる。
165
+
166
+ **規約の使いやすさ**: `@functools.wraps` の必要性は理解しにくい。
167
+ 「デコレーターを作るときは必ず `@functools.wraps` をつける」という規則を覚えれば十分。
168
+
169
+ ### ペルソナ2: ロースキル経験者(Python 歴3-4年・スクリプト系・男性・SES)
170
+
171
+ `functools` は名前を知っていても使いこなせていないことが多い。
172
+ `reduce` は「forループで書いた方が分かる」と言って使わない傾向がある。
173
+
174
+ **コピペ可能性**: `@lru_cache(maxsize=128)` と `@functools.wraps(func)` はそのままコピペできる。
175
+
176
+ **拡張時の罠**: `@lru_cache` の引数が変わったときにキャッシュが古い値を返し続ける。
177
+ 引数の型が mutable(`list`, `dict`)だと `TypeError: unhashable type` が発生することに気づかない。
178
+
179
+ **セキュリティ的な事故リスク**: 低。ただし `@lru_cache` に認証情報を含む引数を渡すと
180
+ 別ユーザーのキャッシュが混入するリスクがある(BOLA の一形態)。
181
+
182
+ ### ペルソナ3: フロントエンド寄り経験者(React/TS 歴4年・バックエンド転向中・ノンバイナリ)
183
+
184
+ `useMemo` / `useCallback` と `@lru_cache` / `@cached_property` は概念が近い。
185
+ 「同じ入力なら再計算しない」という理解はすでにある。
186
+
187
+ **Python 固有概念の学習コスト**: `partial` は JS の `bind` や `curry` に近い概念。
188
+ `reduce` は `Array.prototype.reduce` と同じ。TypeScript ユーザーには直感的。
189
+
190
+ **事故リスク**: 低。
191
+
192
+ ### ペルソナ4: バックエンド経験者(Django/FastAPI 歴5-6年・男性・リードエンジニア)
193
+
194
+ Django の `@method_decorator` / `cache_page` との差異を理解している。
195
+ `@functools.wraps` は既知。`@lru_cache` の eviction ポリシー(LRU)を正確に理解している。
196
+
197
+ **他フレームワークとの差異**: Django は `cache.get/set` で明示的にキャッシュを管理する。
198
+ nene2-python で `@lru_cache` を使う場合、TTL がないことを意識する必要がある。
199
+ TTL が必要なら FT100 で追加した `TtlCache` を使う。
200
+
201
+ **本番投入可能性**: 問題なし。ただしキャッシュキーのサイズとメモリ消費量の設計が必要。
202
+
203
+ ### ペルソナ5: シニアエンジニア(設計・コードレビュー担当・女性・10-12年)
204
+
205
+ **コードレビューチェックポイント**:
206
+ - [ ] `@lru_cache` / `@cache` の引数に mutable 型(list, dict)が渡されていないか(`TypeError` になる)
207
+ - [ ] `@lru_cache` をリクエストスコープで使っていないか(プロセス共有で期待外の値が返る)
208
+ - [ ] `@lru_cache` に認証情報・ユーザーIDが引数に含まれる場合、BOLA にならないか
209
+ - [ ] デコレーターに `@functools.wraps` がついているか(OpenAPI operationId の破損防止)
210
+ - [ ] `@cache`(unbounded)を使う場合、引数の値域が有限か(メモリリーク防止)
211
+
212
+ **チームでの安全なパターン**: キャッシュが必要な UseCase は `cache_clear()` を公開メソッドとして持ち、
213
+ lifespan の shutdown / テストの teardown で明示的にクリアする規約を設ける。
214
+
215
+ ### ペルソナ6: 設計者・ポリシー照合(nene2-python 設計ポリシー目線)
216
+
217
+ **ポリシー達成度**: 高
218
+
219
+ **「初心者でも安全な API」達成度**: 中
220
+ - `@lru_cache` のキャッシュ汚染リスク(テスト・BOLA)をドキュメントが説明していない
221
+ - `@functools.wraps` の必要性が CLAUDE.md に明記されていない(FastAPI との相性問題)
222
+
223
+ **設計上の負債・ドキュメント不足**:
224
+ - nene2-python のカスタムデコレーター作成時の `@functools.wraps` 使用がポリシー化されていない
225
+ - `@lru_cache` の「リクエストスコープ不可・認証情報を引数に含めない」ルールが未文書
226
+
227
+ **Follow-up Issue 候補**: `docs: functools.wraps をカスタムデコレーター規約に追加`
228
+
229
+ ---
230
+
231
+ ## Follow-up Issues
232
+
233
+ | 優先度 | タイトル | 種別 |
234
+ |---|---|---|
235
+ | 中 | `docs: @functools.wraps をデコレーター作成規約として CLAUDE.md に追加` | docs |
236
+ | 低 | `docs: @lru_cache のリクエストスコープ不可・認証情報禁止をキャッシュ how-to に記載` | docs |
237
+
238
+ ---
239
+
240
+ ## まとめ
241
+
242
+ `functools` は nene2-python の DI・キャッシュ・デコレーター実装に広く関連するモジュール。
243
+ 34 テスト全通過、摩擦ゼロ。
244
+ `@cached_property` がインスタンス単位、`@lru_cache` がプロセス単位という使い分けが重要。
245
+ `@functools.wraps` なしのデコレーターは FastAPI の operationId を破壊するため、
246
+ カスタムデコレーター規約への追記が推奨される。
@@ -0,0 +1,239 @@
1
+ # FT167: enum モジュール
2
+
3
+ **日付**: 2026-05-21
4
+ **テーマ**: `enum` モジュール — `Enum`・`StrEnum`・`IntEnum`・`Flag`・`auto()`・状態遷移・権限管理
5
+ **セキュリティ診断**: なし(167 % 3 = 2)
6
+
7
+ ---
8
+
9
+ ## 概要
10
+
11
+ Python 標準ライブラリの `enum` モジュールを nene2-python フレームワーク上で検証した。
12
+ `enum` はドメインモデルの状態・権限・分類を型安全に表現するための中核モジュール。
13
+ `StrEnum`(Python 3.11+)は Pydantic v2 と組み合わせて HTTP 境界での型検証に直接使える。
14
+
15
+ ---
16
+
17
+ ## 実装したサンプルアプリ
18
+
19
+ **場所**: `/home/xi/docker/nene2-python-FT/ft167-enum/`
20
+
21
+ ### 主要機能
22
+
23
+ | クラス/関数 | 概要 |
24
+ |---|---|
25
+ | `Color(Enum)` | 基本 Enum。値で参照・イテレーション |
26
+ | `TaskStatus(StrEnum)` | `auto()` で名前を小文字化。文字列として使用可能 |
27
+ | `Priority(IntEnum)` | 比較・算術演算が可能な整数 Enum |
28
+ | `Permission(Flag)` | ビットフラグで権限を組み合わせ(READ / WRITE / DELETE / ADMIN) |
29
+ | `HttpMethod(StrEnum)` | カスタムプロパティ(`is_safe`, `has_body`)付き |
30
+ | `Task` dataclass | `TaskStatus` / `Priority` / `Permission` を持つドメインオブジェクト |
31
+ | `transition_task()` | 許可された状態遷移のみ受け付ける型安全な状態機械 |
32
+ | `describe_permission()` | `Flag` の組み合わせを人間可読な辞書に変換 |
33
+
34
+ ### HTTP エンドポイント
35
+
36
+ | メソッド | パス | 概要 |
37
+ |---|---|---|
38
+ | GET | `/enum/colors` | 全 Color 一覧 |
39
+ | GET | `/enum/task-status` | TaskStatus 一覧・StrEnum 確認 |
40
+ | POST | `/enum/transition` | 状態遷移バリデーション |
41
+ | GET | `/enum/priority` | Priority 比較 |
42
+ | POST | `/enum/permission` | Permission フラグ組み合わせ |
43
+ | GET | `/enum/http-method` | HttpMethod プロパティ |
44
+
45
+ ---
46
+
47
+ ## テスト結果
48
+
49
+ **32 passed(摩擦ゼロ)**
50
+
51
+ ```
52
+ 32 passed in 1.00s
53
+ ```
54
+
55
+ ---
56
+
57
+ ## 摩擦ポイント
58
+
59
+ **今回の FT では実装上の摩擦はゼロだった。**
60
+
61
+ ---
62
+
63
+ ## 観察点
64
+
65
+ ### 観察1: `StrEnum` + `auto()` で名前を自動的に小文字化
66
+
67
+ ```python
68
+ class TaskStatus(enum.StrEnum):
69
+ PENDING = enum.auto() # → "pending"
70
+ RUNNING = enum.auto() # → "running"
71
+ ```
72
+
73
+ `StrEnum` の `auto()` は `_generate_next_value_()` をオーバーライドして
74
+ メンバー名を小文字にする。つまり `TaskStatus.PENDING == "pending"` が成立し、
75
+ JSON シリアライズ時に `.value` を呼ぶ必要がない。
76
+ Pydantic v2 + FastAPI ではフィールドに `TaskStatus` を指定するだけで HTTP Body・レスポンスで使える。
77
+
78
+ ### 観察2: `Flag` でビット権限管理 — `in` 演算子でチェック
79
+
80
+ ```python
81
+ class Permission(enum.Flag):
82
+ READ = enum.auto() # 1
83
+ WRITE = enum.auto() # 2
84
+ DELETE = enum.auto() # 4
85
+ ADMIN = READ | WRITE | DELETE # 7
86
+
87
+ rw = Permission.READ | Permission.WRITE
88
+ assert Permission.READ in rw # True
89
+ assert Permission.DELETE not in rw # True
90
+ ```
91
+
92
+ `Flag` のビット演算(`|`, `&`, `~`)は直感的で、RBAC の権限チェックに最適。
93
+ `Permission.NONE = 0` を定義することで「権限なし」を明示的に表現できる。
94
+
95
+ ### 観察3: `IntEnum` は `int` として比較・ソートできる
96
+
97
+ ```python
98
+ assert Priority.LOW < Priority.HIGH # True(数値比較)
99
+ assert Priority.LOW + Priority.MEDIUM == 6 # 算術演算も可
100
+ sorted_priorities = sorted([Priority.HIGH, Priority.LOW]) # [LOW, HIGH]
101
+ ```
102
+
103
+ DB のカラム値が整数の場合、`IntEnum` を使うと ORM との相互変換がシームレス。
104
+ ただし算術演算の結果が `int`(Enum でない)になることに注意。
105
+
106
+ ### 観察4: Enum に `@property` を追加してドメインロジックをカプセル化
107
+
108
+ ```python
109
+ class HttpMethod(enum.StrEnum):
110
+ @property
111
+ def is_safe(self) -> bool:
112
+ return self in (HttpMethod.GET,)
113
+
114
+ @property
115
+ def has_body(self) -> bool:
116
+ return self in (HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH)
117
+ ```
118
+
119
+ Enum にメソッドを追加することで、`if method == "GET":` の散在を防ぎ
120
+ ドメインロジックを 1 箇所に集約できる。`@property` との相性も良い。
121
+
122
+ ### 観察5: Pydantic v2 は `StrEnum` を文字列バリデーションとして扱う
123
+
124
+ ```python
125
+ class TransitionBody(BaseModel):
126
+ current_status: TaskStatus # "pending" / "running" / "done" / "failed" のみ許可
127
+ new_status: TaskStatus
128
+ ```
129
+
130
+ Pydantic v2 は `StrEnum` フィールドに対して、有効な enum 値のみを受け付け、
131
+ 無効な値には自動的に 422 を返す。`Literal["pending", "running", ...]` より型安全。
132
+
133
+ ---
134
+
135
+ ## nene2-python フレームワークとの統合
136
+
137
+ - `TaskStatus(StrEnum)` は nene2 の Note ドメインの `status` フィールドに直接適用できる
138
+ - `Permission(Flag)` は `ApiKeyAuthMiddleware` の権限スコープ実装に応用可能
139
+ - `Priority(IntEnum)` はタスクスケジューリング・DB ソートに使いやすい
140
+ - Enum をレスポンスモデルに含める場合、FastAPI は自動的に `.value` でシリアライズする
141
+ - `transition_task()` のようなドメイン状態機械は UseCase 内に実装するパターンが nene2 アーキテクチャと整合する
142
+
143
+ ---
144
+
145
+ ## Developer Experience (DX) Review
146
+
147
+ ### ペルソナ1: 初心者(Python 歴1年・独学中・女性・バックエンド志望)
148
+
149
+ `Enum` の基本(`class Color(Enum): RED = "red"`)は直感的に理解できる。
150
+ `StrEnum`・`IntEnum`・`Flag` の使い分けは最初は混乱する。
151
+
152
+ **ドキュメント理解**: 「どれを使えばいいか」の判断基準がなければ基本 `Enum` だけを使い続ける。
153
+ nene2 の how-to に「HTTP API の状態フィールドは `StrEnum` を使う」という指針があれば一発で決まる。
154
+
155
+ **事故リスク**: 低。Enum の誤用は大きな事故にはつながりにくい。
156
+ ただし `IntEnum` の算術演算結果が `int` になることを知らず、Enum メンバーを期待してバグになる可能性。
157
+
158
+ **規約の使いやすさ**: `StrEnum` + `auto()` のパターンはコピペで使える。
159
+
160
+ ### ペルソナ2: ロースキル経験者(Python 歴3-4年・スクリプト系・男性・SES)
161
+
162
+ 文字列定数の代わりに `"pending"` / `"running"` をそのまま使う習慣がある。
163
+ `StrEnum` を知ると「これだけで typo を防げるんだ」と即採用する傾向。
164
+
165
+ **コピペ可能性**: `StrEnum` + `auto()` のパターンは一度見れば再現できる。
166
+
167
+ **拡張時の罠**: `Flag` の複合演算(`rw = READ | WRITE`)を見て、
168
+ 「`|` は OR だから `rw == READ or rw == WRITE` と同じ」と誤解し、
169
+ `if rw == READ:` と書いてしまう(`in` を使わなければならない)。
170
+
171
+ **セキュリティ的な事故リスク**: 中。`Permission.ADMIN = READ | WRITE | DELETE` を定義したつもりが、
172
+ ビット演算の順序ミスで意図しない権限の組み合わせになるリスク。テストで確認が必要。
173
+
174
+ ### ペルソナ3: フロントエンド寄り経験者(React/TS 歴4年・バックエンド転向中・ノンバイナリ)
175
+
176
+ TypeScript の `enum` / `const enum` / `union type` との比較で混乱する可能性。
177
+ Python の `Enum` はクラスであり、TS の `enum` より強力(メソッド追加可能)。
178
+
179
+ **エラーレスポンスの質**: Pydantic が `StrEnum` を自動バリデーションして 422 を返すため、
180
+ クライアントには明確なエラーが返る。
181
+
182
+ **事故リスク**: 低。TS の概念と対応関係が作れる。
183
+
184
+ ### ペルソナ4: バックエンド経験者(Django/FastAPI 歴5-6年・男性・リードエンジニア)
185
+
186
+ Django の `CharField(choices=...)` + `TextChoices` との比較で評価する。
187
+ `StrEnum` は `TextChoices` より汎用的で型安全性が高い。
188
+
189
+ **他フレームワークとの差異**:
190
+ - Django: `models.TextChoices` → DB のチョイスとモデルを一元管理
191
+ - nene2-python: `StrEnum` → HTTP 境界と UseCase で型安全に使い、Repository が DB マッピングを担当
192
+ - 関心の分離がより明確
193
+
194
+ **本番投入可能性**: 問題なし。`StrEnum` は SQLAlchemy の `Enum` 型と組み合わせると完璧。
195
+
196
+ ### ペルソナ5: シニアエンジニア(設計・コードレビュー担当・女性・10-12年)
197
+
198
+ **コードレビューチェックポイント**:
199
+ - [ ] 状態遷移が `transition_task()` のように集約されているか(散在した `if status == "done":` を禁止)
200
+ - [ ] `Flag` の権限チェックで `perm == Permission.READ`(完全一致)でなく `Permission.READ in perm`(部分一致)を使っているか
201
+ - [ ] `IntEnum` の算術演算結果が `int` になることをコードが前提にしていないか
202
+ - [ ] Enum メンバーを文字列比較(`status == "pending"`)でなく Enum 比較(`status == TaskStatus.PENDING`)しているか
203
+ - [ ] DB に保存する Enum 値は `.value` を使って保存し、読み出し時に Enum に変換しているか
204
+
205
+ **チームでの安全なパターン**: 状態遷移グラフは `dict[Status, set[Status]]` で明示的に宣言し、
206
+ UseCase の外部に公開する(テストしやすくなる)。
207
+
208
+ ### ペルソナ6: 設計者・ポリシー照合(nene2-python 設計ポリシー目線)
209
+
210
+ **ポリシー達成度**: 高
211
+
212
+ **「初心者でも安全な API」達成度**: 高
213
+ - Pydantic + `StrEnum` の組み合わせにより、HTTP 境界での自動バリデーションが確立される
214
+ - 無効な状態値は 422 で自動的に拒否される
215
+
216
+ **設計上の負債・ドキュメント不足**:
217
+ - nene2 の example ドメイン(Note / Tag / Comment)に `StrEnum` を使っているフィールドがない
218
+ - Note の `status` フィールドに `StrEnum` を導入するサンプルがあると良い
219
+
220
+ **Follow-up Issue 候補**: `docs: StrEnum を使ったドメイン状態管理の how-to を追加`
221
+
222
+ ---
223
+
224
+ ## Follow-up Issues
225
+
226
+ | 優先度 | タイトル | 種別 |
227
+ |---|---|---|
228
+ | 中 | `docs: StrEnum + Pydantic + 状態遷移パターンの how-to を追加` | docs |
229
+ | 低 | `feat: example/note に status フィールド(StrEnum)を追加してパターンを実演` | feat |
230
+
231
+ ---
232
+
233
+ ## まとめ
234
+
235
+ `enum` モジュールは nene2-python のドメインモデル設計に直接適用できる重要な機能。
236
+ `StrEnum` + Pydantic v2 の組み合わせで HTTP 境界の自動バリデーションが完成し、
237
+ `Flag` による権限管理は RBAC の実装基盤になる。
238
+ 32 テスト全通過、摩擦ゼロ。
239
+ 状態遷移を `dict[Status, set[Status]]` で宣言するパターンは UseCase 内で採用が推奨される。
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nene2-python"
3
- version = "1.8.36"
3
+ version = "1.8.38"
4
4
  description = "NENE2 Python — minimal API framework following NENE2's design philosophy"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes