nene2-python 1.8.37__tar.gz → 1.8.39__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 (389) hide show
  1. {nene2_python-1.8.37 → nene2_python-1.8.39}/PKG-INFO +1 -1
  2. nene2_python-1.8.39/docs/field-trials/2026-05-field-trial-167.md +239 -0
  3. nene2_python-1.8.39/docs/field-trials/2026-05-field-trial-168.md +473 -0
  4. {nene2_python-1.8.37 → nene2_python-1.8.39}/pyproject.toml +1 -1
  5. {nene2_python-1.8.37 → nene2_python-1.8.39}/uv.lock +1 -1
  6. {nene2_python-1.8.37 → nene2_python-1.8.39}/.env.example +0 -0
  7. {nene2_python-1.8.37 → nene2_python-1.8.39}/.github/workflows/ci.yml +0 -0
  8. {nene2_python-1.8.37 → nene2_python-1.8.39}/.github/workflows/docs.yml +0 -0
  9. {nene2_python-1.8.37 → nene2_python-1.8.39}/.github/workflows/publish.yml +0 -0
  10. {nene2_python-1.8.37 → nene2_python-1.8.39}/.gitignore +0 -0
  11. {nene2_python-1.8.37 → nene2_python-1.8.39}/.vitepress/config.mts +0 -0
  12. {nene2_python-1.8.37 → nene2_python-1.8.39}/.vitepress/theme/custom.css +0 -0
  13. {nene2_python-1.8.37 → nene2_python-1.8.39}/.vitepress/theme/index.ts +0 -0
  14. {nene2_python-1.8.37 → nene2_python-1.8.39}/AGENTS.md +0 -0
  15. {nene2_python-1.8.37 → nene2_python-1.8.39}/CHANGELOG.md +0 -0
  16. {nene2_python-1.8.37 → nene2_python-1.8.39}/CLAUDE.md +0 -0
  17. {nene2_python-1.8.37 → nene2_python-1.8.39}/Dockerfile +0 -0
  18. {nene2_python-1.8.37 → nene2_python-1.8.39}/LICENSE +0 -0
  19. {nene2_python-1.8.37 → nene2_python-1.8.39}/README.md +0 -0
  20. {nene2_python-1.8.37 → nene2_python-1.8.39}/alembic/README +0 -0
  21. {nene2_python-1.8.37 → nene2_python-1.8.39}/alembic/env.py +0 -0
  22. {nene2_python-1.8.37 → nene2_python-1.8.39}/alembic/script.py.mako +0 -0
  23. {nene2_python-1.8.37 → nene2_python-1.8.39}/alembic/versions/001_create_notes_and_tags_tables.py +0 -0
  24. {nene2_python-1.8.37 → nene2_python-1.8.39}/alembic.ini +0 -0
  25. {nene2_python-1.8.37 → nene2_python-1.8.39}/compose.yaml +0 -0
  26. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/adr/0001-toolchain.md +0 -0
  27. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/adr/0002-clean-architecture.md +0 -0
  28. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/adr/0003-security-first.md +0 -0
  29. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/adr/0004-ai-first-design.md +0 -0
  30. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/adr/0005-logging.md +0 -0
  31. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/adr/0006-rate-limiting.md +0 -0
  32. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/adr/0009-mcp-design.md +0 -0
  33. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/adr/0010-async-use-case.md +0 -0
  34. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/adr/0011-mcp-as-core-dependency.md +0 -0
  35. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/de/index.md +0 -0
  36. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/de/tutorials/getting-started.md +0 -0
  37. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/explanation/architecture.md +0 -0
  38. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/explanation/design-philosophy.md +0 -0
  39. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-1.md +0 -0
  40. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-10.md +0 -0
  41. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-100.md +0 -0
  42. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-101.md +0 -0
  43. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-102.md +0 -0
  44. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-103.md +0 -0
  45. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-104.md +0 -0
  46. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-105.md +0 -0
  47. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-106.md +0 -0
  48. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-107.md +0 -0
  49. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-108.md +0 -0
  50. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-109.md +0 -0
  51. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-11.md +0 -0
  52. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-110.md +0 -0
  53. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-111.md +0 -0
  54. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-112.md +0 -0
  55. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-113.md +0 -0
  56. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-114.md +0 -0
  57. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-115.md +0 -0
  58. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-116.md +0 -0
  59. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-117.md +0 -0
  60. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-118.md +0 -0
  61. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-119.md +0 -0
  62. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-12.md +0 -0
  63. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-120.md +0 -0
  64. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-121.md +0 -0
  65. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-122.md +0 -0
  66. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-123.md +0 -0
  67. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-124.md +0 -0
  68. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-125.md +0 -0
  69. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-126.md +0 -0
  70. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-127.md +0 -0
  71. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-128.md +0 -0
  72. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-129.md +0 -0
  73. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-13.md +0 -0
  74. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-130.md +0 -0
  75. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-131.md +0 -0
  76. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-132.md +0 -0
  77. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-133.md +0 -0
  78. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-134.md +0 -0
  79. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-135.md +0 -0
  80. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-136.md +0 -0
  81. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-137.md +0 -0
  82. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-138.md +0 -0
  83. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-139.md +0 -0
  84. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-14.md +0 -0
  85. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-140.md +0 -0
  86. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-141.md +0 -0
  87. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-142.md +0 -0
  88. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-143.md +0 -0
  89. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-144.md +0 -0
  90. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-145.md +0 -0
  91. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-146.md +0 -0
  92. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-147.md +0 -0
  93. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-148.md +0 -0
  94. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-149.md +0 -0
  95. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-15.md +0 -0
  96. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-150.md +0 -0
  97. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-151.md +0 -0
  98. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-152.md +0 -0
  99. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-153.md +0 -0
  100. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-154.md +0 -0
  101. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-155.md +0 -0
  102. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-156.md +0 -0
  103. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-157.md +0 -0
  104. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-158.md +0 -0
  105. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-159.md +0 -0
  106. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-16.md +0 -0
  107. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-160.md +0 -0
  108. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-161.md +0 -0
  109. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-162.md +0 -0
  110. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-163.md +0 -0
  111. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-164.md +0 -0
  112. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-165.md +0 -0
  113. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-166.md +0 -0
  114. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-17.md +0 -0
  115. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-18.md +0 -0
  116. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-19.md +0 -0
  117. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-2.md +0 -0
  118. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-20.md +0 -0
  119. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-21.md +0 -0
  120. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-22.md +0 -0
  121. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-23.md +0 -0
  122. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-24.md +0 -0
  123. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-25.md +0 -0
  124. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-26.md +0 -0
  125. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-27.md +0 -0
  126. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-28.md +0 -0
  127. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-29.md +0 -0
  128. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-3.md +0 -0
  129. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-30.md +0 -0
  130. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-31.md +0 -0
  131. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-32.md +0 -0
  132. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-33.md +0 -0
  133. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-34.md +0 -0
  134. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-35.md +0 -0
  135. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-36.md +0 -0
  136. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-37.md +0 -0
  137. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-38.md +0 -0
  138. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-39.md +0 -0
  139. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-4.md +0 -0
  140. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-40.md +0 -0
  141. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-41.md +0 -0
  142. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-42.md +0 -0
  143. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-43.md +0 -0
  144. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-44.md +0 -0
  145. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-45.md +0 -0
  146. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-46.md +0 -0
  147. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-47.md +0 -0
  148. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-48.md +0 -0
  149. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-49.md +0 -0
  150. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-5.md +0 -0
  151. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-50.md +0 -0
  152. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-51.md +0 -0
  153. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-52.md +0 -0
  154. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-53.md +0 -0
  155. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-54.md +0 -0
  156. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-55.md +0 -0
  157. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-56.md +0 -0
  158. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-57.md +0 -0
  159. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-58.md +0 -0
  160. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-59.md +0 -0
  161. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-6.md +0 -0
  162. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-60.md +0 -0
  163. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-61.md +0 -0
  164. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-62.md +0 -0
  165. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-63.md +0 -0
  166. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-64.md +0 -0
  167. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-65.md +0 -0
  168. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-66.md +0 -0
  169. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-67.md +0 -0
  170. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-68.md +0 -0
  171. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-69.md +0 -0
  172. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-7.md +0 -0
  173. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-70.md +0 -0
  174. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-71.md +0 -0
  175. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-72.md +0 -0
  176. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-73.md +0 -0
  177. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-74.md +0 -0
  178. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-75.md +0 -0
  179. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-76.md +0 -0
  180. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-77.md +0 -0
  181. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-78.md +0 -0
  182. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-79.md +0 -0
  183. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-8.md +0 -0
  184. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-80.md +0 -0
  185. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-81.md +0 -0
  186. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-82.md +0 -0
  187. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-83.md +0 -0
  188. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-84.md +0 -0
  189. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-85.md +0 -0
  190. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-86.md +0 -0
  191. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-87.md +0 -0
  192. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-88.md +0 -0
  193. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-89.md +0 -0
  194. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-9.md +0 -0
  195. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-90.md +0 -0
  196. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-91.md +0 -0
  197. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-92.md +0 -0
  198. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-93.md +0 -0
  199. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-94.md +0 -0
  200. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-95.md +0 -0
  201. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-96.md +0 -0
  202. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-97.md +0 -0
  203. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-98.md +0 -0
  204. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/field-trials/2026-05-field-trial-99.md +0 -0
  205. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/fr/index.md +0 -0
  206. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/fr/tutorials/getting-started.md +0 -0
  207. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/add-new-domain.md +0 -0
  208. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/api-versioning.md +0 -0
  209. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/async-use-case.md +0 -0
  210. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/background-tasks.md +0 -0
  211. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/configure-auth.md +0 -0
  212. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/cors.md +0 -0
  213. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/custom-auth-middleware.md +0 -0
  214. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/dependency-injection.md +0 -0
  215. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/domain-events.md +0 -0
  216. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/file-upload.md +0 -0
  217. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/lifespan-and-app-state.md +0 -0
  218. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/middleware-stack.md +0 -0
  219. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/new-project.md +0 -0
  220. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/problem-details.md +0 -0
  221. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/response-patterns.md +0 -0
  222. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/run-tests.md +0 -0
  223. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/soft-delete.md +0 -0
  224. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/sqlalchemy-repository.md +0 -0
  225. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/streaming.md +0 -0
  226. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/structured-logging.md +0 -0
  227. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/validation.md +0 -0
  228. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/how-to/webhook.md +0 -0
  229. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/howto/mcp-setup.md +0 -0
  230. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/index.md +0 -0
  231. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/explanation/architecture.md +0 -0
  232. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/explanation/design-philosophy.md +0 -0
  233. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/how-to/add-new-domain.md +0 -0
  234. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/how-to/configure-auth.md +0 -0
  235. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/how-to/new-project.md +0 -0
  236. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/how-to/run-tests.md +0 -0
  237. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/how-to/sqlalchemy-repository.md +0 -0
  238. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/howto/mcp-setup.md +0 -0
  239. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/index.md +0 -0
  240. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/reference/api.md +0 -0
  241. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/reference/configuration.md +0 -0
  242. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/reference/framework-modules.md +0 -0
  243. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/tutorials/first-domain.md +0 -0
  244. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/ja/tutorials/getting-started.md +0 -0
  245. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/pt-br/index.md +0 -0
  246. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/pt-br/tutorials/getting-started.md +0 -0
  247. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/reference/api.md +0 -0
  248. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/reference/configuration.md +0 -0
  249. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/reference/framework-modules.md +0 -0
  250. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/roadmap.md +0 -0
  251. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/templates/field-trial-report.md +0 -0
  252. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/todo/current.md +0 -0
  253. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/tutorials/first-domain.md +0 -0
  254. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/tutorials/getting-started.md +0 -0
  255. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/zh/index.md +0 -0
  256. {nene2_python-1.8.37 → nene2_python-1.8.39}/docs/zh/tutorials/getting-started.md +0 -0
  257. {nene2_python-1.8.37 → nene2_python-1.8.39}/package-lock.json +0 -0
  258. {nene2_python-1.8.37 → nene2_python-1.8.39}/package.json +0 -0
  259. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/__init__.py +0 -0
  260. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/__main__.py +0 -0
  261. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/app.py +0 -0
  262. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/comment/__init__.py +0 -0
  263. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/comment/entity.py +0 -0
  264. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/comment/exceptions.py +0 -0
  265. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/comment/handler.py +0 -0
  266. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/comment/repository.py +0 -0
  267. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/comment/sqlalchemy_repository.py +0 -0
  268. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/comment/use_case.py +0 -0
  269. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/mcp.py +0 -0
  270. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/note/__init__.py +0 -0
  271. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/note/async_use_case.py +0 -0
  272. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/note/entity.py +0 -0
  273. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/note/exceptions.py +0 -0
  274. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/note/handler.py +0 -0
  275. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/note/repository.py +0 -0
  276. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/note/sqlalchemy_repository.py +0 -0
  277. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/note/use_case.py +0 -0
  278. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/schema.py +0 -0
  279. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/tag/__init__.py +0 -0
  280. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/tag/entity.py +0 -0
  281. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/tag/exceptions.py +0 -0
  282. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/tag/handler.py +0 -0
  283. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/tag/repository.py +0 -0
  284. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/tag/sqlalchemy_repository.py +0 -0
  285. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/example/tag/use_case.py +0 -0
  286. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/__init__.py +0 -0
  287. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/auth/__init__.py +0 -0
  288. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/auth/api_key.py +0 -0
  289. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/auth/bearer_token.py +0 -0
  290. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/auth/deps.py +0 -0
  291. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/auth/exceptions.py +0 -0
  292. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/auth/interfaces.py +0 -0
  293. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/auth/local_verifier.py +0 -0
  294. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/cache/__init__.py +0 -0
  295. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/cache/ttl.py +0 -0
  296. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/config/__init__.py +0 -0
  297. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/config/settings.py +0 -0
  298. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/database/__init__.py +0 -0
  299. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/database/exceptions.py +0 -0
  300. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/database/health.py +0 -0
  301. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/database/interfaces.py +0 -0
  302. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/database/sqlalchemy_executor.py +0 -0
  303. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/database/utils.py +0 -0
  304. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/http/__init__.py +0 -0
  305. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/http/etag.py +0 -0
  306. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/http/health.py +0 -0
  307. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/http/pagination.py +0 -0
  308. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/http/problem_details.py +0 -0
  309. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/log/__init__.py +0 -0
  310. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/log/setup.py +0 -0
  311. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/mcp/__init__.py +0 -0
  312. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/mcp/http_client.py +0 -0
  313. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/mcp/server.py +0 -0
  314. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/middleware/__init__.py +0 -0
  315. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/middleware/domain_exception.py +0 -0
  316. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/middleware/error_handler.py +0 -0
  317. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/middleware/request_id.py +0 -0
  318. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/middleware/request_logging.py +0 -0
  319. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/middleware/request_size_limit.py +0 -0
  320. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/middleware/security_headers.py +0 -0
  321. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/middleware/setup.py +0 -0
  322. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/middleware/throttle.py +0 -0
  323. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/py.typed +0 -0
  324. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/security/__init__.py +0 -0
  325. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/security/webhook.py +0 -0
  326. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/use_case/__init__.py +0 -0
  327. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/use_case/protocols.py +0 -0
  328. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/validation/__init__.py +0 -0
  329. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/nene2/validation/exceptions.py +0 -0
  330. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/scripts/__init__.py +0 -0
  331. {nene2_python-1.8.37 → nene2_python-1.8.39}/src/scripts/export_openapi.py +0 -0
  332. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/__init__.py +0 -0
  333. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/conftest.py +0 -0
  334. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/__init__.py +0 -0
  335. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/comment/__init__.py +0 -0
  336. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/comment/test_comment_http.py +0 -0
  337. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/comment/test_comment_repository.py +0 -0
  338. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/comment/test_comment_use_case.py +0 -0
  339. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/conftest.py +0 -0
  340. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/note/__init__.py +0 -0
  341. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/note/test_async_note_use_case.py +0 -0
  342. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/note/test_list_notes.py +0 -0
  343. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/note/test_note_repository.py +0 -0
  344. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/tag/__init__.py +0 -0
  345. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/tag/test_tag_repository.py +0 -0
  346. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/tag/test_tags.py +0 -0
  347. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/test_cors.py +0 -0
  348. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/example/test_mcp.py +0 -0
  349. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/__init__.py +0 -0
  350. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/auth/__init__.py +0 -0
  351. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/auth/test_api_key.py +0 -0
  352. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/auth/test_bearer_token.py +0 -0
  353. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/auth/test_make_require_auth.py +0 -0
  354. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/auth/test_token_issuer.py +0 -0
  355. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/cache/__init__.py +0 -0
  356. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/cache/test_ttl.py +0 -0
  357. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/config/__init__.py +0 -0
  358. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/config/test_settings.py +0 -0
  359. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/database/__init__.py +0 -0
  360. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/database/test_transaction.py +0 -0
  361. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/database/test_utils.py +0 -0
  362. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/http/__init__.py +0 -0
  363. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/http/test_etag.py +0 -0
  364. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/http/test_health.py +0 -0
  365. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/http/test_pagination.py +0 -0
  366. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/http/test_problem_details.py +0 -0
  367. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/log/__init__.py +0 -0
  368. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/log/test_setup.py +0 -0
  369. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/mcp/__init__.py +0 -0
  370. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/mcp/test_http_client.py +0 -0
  371. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/mcp/test_server.py +0 -0
  372. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/middleware/__init__.py +0 -0
  373. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/middleware/test_error_handler.py +0 -0
  374. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/middleware/test_request_id.py +0 -0
  375. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/middleware/test_request_logging.py +0 -0
  376. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/middleware/test_request_size_limit.py +0 -0
  377. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/middleware/test_security_headers.py +0 -0
  378. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/middleware/test_setup_middlewares.py +0 -0
  379. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/middleware/test_simple_domain_handler.py +0 -0
  380. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/middleware/test_throttle.py +0 -0
  381. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/security/__init__.py +0 -0
  382. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/security/test_webhook.py +0 -0
  383. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/use_case/__init__.py +0 -0
  384. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/use_case/test_protocols.py +0 -0
  385. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/use_case/test_run_in_threadpool.py +0 -0
  386. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/validation/__init__.py +0 -0
  387. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/nene2/validation/test_exceptions.py +0 -0
  388. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/scripts/__init__.py +0 -0
  389. {nene2_python-1.8.37 → nene2_python-1.8.39}/tests/scripts/test_export_openapi.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nene2-python
3
- Version: 1.8.37
3
+ Version: 1.8.39
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,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 内で採用が推奨される。