nene2-python 1.8.46__tar.gz → 1.8.47__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 (397) hide show
  1. {nene2_python-1.8.46 → nene2_python-1.8.47}/PKG-INFO +1 -1
  2. nene2_python-1.8.47/docs/field-trials/2026-05-field-trial-176.md +341 -0
  3. {nene2_python-1.8.46 → nene2_python-1.8.47}/pyproject.toml +1 -1
  4. {nene2_python-1.8.46 → nene2_python-1.8.47}/.env.example +0 -0
  5. {nene2_python-1.8.46 → nene2_python-1.8.47}/.github/workflows/ci.yml +0 -0
  6. {nene2_python-1.8.46 → nene2_python-1.8.47}/.github/workflows/docs.yml +0 -0
  7. {nene2_python-1.8.46 → nene2_python-1.8.47}/.github/workflows/publish.yml +0 -0
  8. {nene2_python-1.8.46 → nene2_python-1.8.47}/.gitignore +0 -0
  9. {nene2_python-1.8.46 → nene2_python-1.8.47}/.vitepress/config.mts +0 -0
  10. {nene2_python-1.8.46 → nene2_python-1.8.47}/.vitepress/theme/custom.css +0 -0
  11. {nene2_python-1.8.46 → nene2_python-1.8.47}/.vitepress/theme/index.ts +0 -0
  12. {nene2_python-1.8.46 → nene2_python-1.8.47}/AGENTS.md +0 -0
  13. {nene2_python-1.8.46 → nene2_python-1.8.47}/CHANGELOG.md +0 -0
  14. {nene2_python-1.8.46 → nene2_python-1.8.47}/CLAUDE.md +0 -0
  15. {nene2_python-1.8.46 → nene2_python-1.8.47}/Dockerfile +0 -0
  16. {nene2_python-1.8.46 → nene2_python-1.8.47}/LICENSE +0 -0
  17. {nene2_python-1.8.46 → nene2_python-1.8.47}/README.md +0 -0
  18. {nene2_python-1.8.46 → nene2_python-1.8.47}/alembic/README +0 -0
  19. {nene2_python-1.8.46 → nene2_python-1.8.47}/alembic/env.py +0 -0
  20. {nene2_python-1.8.46 → nene2_python-1.8.47}/alembic/script.py.mako +0 -0
  21. {nene2_python-1.8.46 → nene2_python-1.8.47}/alembic/versions/001_create_notes_and_tags_tables.py +0 -0
  22. {nene2_python-1.8.46 → nene2_python-1.8.47}/alembic.ini +0 -0
  23. {nene2_python-1.8.46 → nene2_python-1.8.47}/compose.yaml +0 -0
  24. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/adr/0001-toolchain.md +0 -0
  25. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/adr/0002-clean-architecture.md +0 -0
  26. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/adr/0003-security-first.md +0 -0
  27. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/adr/0004-ai-first-design.md +0 -0
  28. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/adr/0005-logging.md +0 -0
  29. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/adr/0006-rate-limiting.md +0 -0
  30. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/adr/0009-mcp-design.md +0 -0
  31. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/adr/0010-async-use-case.md +0 -0
  32. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/adr/0011-mcp-as-core-dependency.md +0 -0
  33. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/de/index.md +0 -0
  34. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/de/tutorials/getting-started.md +0 -0
  35. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/explanation/architecture.md +0 -0
  36. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/explanation/design-philosophy.md +0 -0
  37. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-1.md +0 -0
  38. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-10.md +0 -0
  39. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-100.md +0 -0
  40. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-101.md +0 -0
  41. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-102.md +0 -0
  42. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-103.md +0 -0
  43. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-104.md +0 -0
  44. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-105.md +0 -0
  45. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-106.md +0 -0
  46. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-107.md +0 -0
  47. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-108.md +0 -0
  48. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-109.md +0 -0
  49. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-11.md +0 -0
  50. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-110.md +0 -0
  51. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-111.md +0 -0
  52. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-112.md +0 -0
  53. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-113.md +0 -0
  54. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-114.md +0 -0
  55. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-115.md +0 -0
  56. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-116.md +0 -0
  57. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-117.md +0 -0
  58. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-118.md +0 -0
  59. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-119.md +0 -0
  60. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-12.md +0 -0
  61. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-120.md +0 -0
  62. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-121.md +0 -0
  63. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-122.md +0 -0
  64. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-123.md +0 -0
  65. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-124.md +0 -0
  66. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-125.md +0 -0
  67. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-126.md +0 -0
  68. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-127.md +0 -0
  69. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-128.md +0 -0
  70. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-129.md +0 -0
  71. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-13.md +0 -0
  72. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-130.md +0 -0
  73. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-131.md +0 -0
  74. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-132.md +0 -0
  75. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-133.md +0 -0
  76. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-134.md +0 -0
  77. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-135.md +0 -0
  78. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-136.md +0 -0
  79. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-137.md +0 -0
  80. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-138.md +0 -0
  81. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-139.md +0 -0
  82. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-14.md +0 -0
  83. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-140.md +0 -0
  84. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-141.md +0 -0
  85. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-142.md +0 -0
  86. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-143.md +0 -0
  87. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-144.md +0 -0
  88. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-145.md +0 -0
  89. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-146.md +0 -0
  90. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-147.md +0 -0
  91. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-148.md +0 -0
  92. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-149.md +0 -0
  93. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-15.md +0 -0
  94. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-150.md +0 -0
  95. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-151.md +0 -0
  96. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-152.md +0 -0
  97. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-153.md +0 -0
  98. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-154.md +0 -0
  99. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-155.md +0 -0
  100. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-156.md +0 -0
  101. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-157.md +0 -0
  102. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-158.md +0 -0
  103. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-159.md +0 -0
  104. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-16.md +0 -0
  105. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-160.md +0 -0
  106. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-161.md +0 -0
  107. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-162.md +0 -0
  108. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-163.md +0 -0
  109. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-164.md +0 -0
  110. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-165.md +0 -0
  111. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-166.md +0 -0
  112. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-167.md +0 -0
  113. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-168.md +0 -0
  114. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-169.md +0 -0
  115. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-17.md +0 -0
  116. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-170.md +0 -0
  117. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-171.md +0 -0
  118. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-172.md +0 -0
  119. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-173.md +0 -0
  120. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-174.md +0 -0
  121. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-175.md +0 -0
  122. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-18.md +0 -0
  123. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-19.md +0 -0
  124. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-2.md +0 -0
  125. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-20.md +0 -0
  126. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-21.md +0 -0
  127. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-22.md +0 -0
  128. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-23.md +0 -0
  129. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-24.md +0 -0
  130. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-25.md +0 -0
  131. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-26.md +0 -0
  132. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-27.md +0 -0
  133. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-28.md +0 -0
  134. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-29.md +0 -0
  135. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-3.md +0 -0
  136. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-30.md +0 -0
  137. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-31.md +0 -0
  138. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-32.md +0 -0
  139. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-33.md +0 -0
  140. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-34.md +0 -0
  141. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-35.md +0 -0
  142. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-36.md +0 -0
  143. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-37.md +0 -0
  144. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-38.md +0 -0
  145. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-39.md +0 -0
  146. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-4.md +0 -0
  147. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-40.md +0 -0
  148. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-41.md +0 -0
  149. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-42.md +0 -0
  150. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-43.md +0 -0
  151. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-44.md +0 -0
  152. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-45.md +0 -0
  153. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-46.md +0 -0
  154. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-47.md +0 -0
  155. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-48.md +0 -0
  156. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-49.md +0 -0
  157. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-5.md +0 -0
  158. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-50.md +0 -0
  159. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-51.md +0 -0
  160. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-52.md +0 -0
  161. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-53.md +0 -0
  162. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-54.md +0 -0
  163. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-55.md +0 -0
  164. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-56.md +0 -0
  165. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-57.md +0 -0
  166. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-58.md +0 -0
  167. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-59.md +0 -0
  168. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-6.md +0 -0
  169. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-60.md +0 -0
  170. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-61.md +0 -0
  171. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-62.md +0 -0
  172. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-63.md +0 -0
  173. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-64.md +0 -0
  174. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-65.md +0 -0
  175. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-66.md +0 -0
  176. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-67.md +0 -0
  177. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-68.md +0 -0
  178. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-69.md +0 -0
  179. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-7.md +0 -0
  180. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-70.md +0 -0
  181. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-71.md +0 -0
  182. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-72.md +0 -0
  183. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-73.md +0 -0
  184. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-74.md +0 -0
  185. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-75.md +0 -0
  186. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-76.md +0 -0
  187. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-77.md +0 -0
  188. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-78.md +0 -0
  189. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-79.md +0 -0
  190. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-8.md +0 -0
  191. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-80.md +0 -0
  192. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-81.md +0 -0
  193. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-82.md +0 -0
  194. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-83.md +0 -0
  195. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-84.md +0 -0
  196. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-85.md +0 -0
  197. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-86.md +0 -0
  198. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-87.md +0 -0
  199. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-88.md +0 -0
  200. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-89.md +0 -0
  201. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-9.md +0 -0
  202. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-90.md +0 -0
  203. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-91.md +0 -0
  204. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-92.md +0 -0
  205. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-93.md +0 -0
  206. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-94.md +0 -0
  207. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-95.md +0 -0
  208. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-96.md +0 -0
  209. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-97.md +0 -0
  210. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-98.md +0 -0
  211. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/field-trials/2026-05-field-trial-99.md +0 -0
  212. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/fr/index.md +0 -0
  213. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/fr/tutorials/getting-started.md +0 -0
  214. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/how-to/add-new-domain.md +0 -0
  215. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/how-to/api-versioning.md +0 -0
  216. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/how-to/async-use-case.md +0 -0
  217. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/how-to/background-tasks.md +0 -0
  218. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/how-to/configure-auth.md +0 -0
  219. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/how-to/cors.md +0 -0
  220. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/how-to/custom-auth-middleware.md +0 -0
  221. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/how-to/dependency-injection.md +0 -0
  222. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/how-to/domain-events.md +0 -0
  223. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/how-to/file-upload.md +0 -0
  224. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/how-to/lifespan-and-app-state.md +0 -0
  225. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/how-to/middleware-stack.md +0 -0
  226. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/how-to/new-project.md +0 -0
  227. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/how-to/problem-details.md +0 -0
  228. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/how-to/response-patterns.md +0 -0
  229. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/how-to/run-tests.md +0 -0
  230. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/how-to/soft-delete.md +0 -0
  231. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/how-to/sqlalchemy-repository.md +0 -0
  232. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/how-to/streaming.md +0 -0
  233. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/how-to/structured-logging.md +0 -0
  234. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/how-to/validation.md +0 -0
  235. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/how-to/webhook.md +0 -0
  236. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/howto/mcp-setup.md +0 -0
  237. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/index.md +0 -0
  238. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/ja/explanation/architecture.md +0 -0
  239. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/ja/explanation/design-philosophy.md +0 -0
  240. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/ja/how-to/add-new-domain.md +0 -0
  241. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/ja/how-to/configure-auth.md +0 -0
  242. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/ja/how-to/new-project.md +0 -0
  243. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/ja/how-to/run-tests.md +0 -0
  244. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/ja/how-to/sqlalchemy-repository.md +0 -0
  245. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/ja/howto/mcp-setup.md +0 -0
  246. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/ja/index.md +0 -0
  247. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/ja/reference/api.md +0 -0
  248. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/ja/reference/configuration.md +0 -0
  249. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/ja/reference/framework-modules.md +0 -0
  250. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/ja/tutorials/first-domain.md +0 -0
  251. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/ja/tutorials/getting-started.md +0 -0
  252. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/pt-br/index.md +0 -0
  253. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/pt-br/tutorials/getting-started.md +0 -0
  254. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/reference/api.md +0 -0
  255. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/reference/configuration.md +0 -0
  256. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/reference/framework-modules.md +0 -0
  257. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/roadmap.md +0 -0
  258. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/templates/field-trial-report.md +0 -0
  259. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/todo/current.md +0 -0
  260. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/tutorials/first-domain.md +0 -0
  261. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/tutorials/getting-started.md +0 -0
  262. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/zh/index.md +0 -0
  263. {nene2_python-1.8.46 → nene2_python-1.8.47}/docs/zh/tutorials/getting-started.md +0 -0
  264. {nene2_python-1.8.46 → nene2_python-1.8.47}/package-lock.json +0 -0
  265. {nene2_python-1.8.46 → nene2_python-1.8.47}/package.json +0 -0
  266. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/__init__.py +0 -0
  267. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/__main__.py +0 -0
  268. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/app.py +0 -0
  269. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/comment/__init__.py +0 -0
  270. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/comment/entity.py +0 -0
  271. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/comment/exceptions.py +0 -0
  272. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/comment/handler.py +0 -0
  273. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/comment/repository.py +0 -0
  274. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/comment/sqlalchemy_repository.py +0 -0
  275. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/comment/use_case.py +0 -0
  276. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/mcp.py +0 -0
  277. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/note/__init__.py +0 -0
  278. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/note/async_use_case.py +0 -0
  279. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/note/entity.py +0 -0
  280. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/note/exceptions.py +0 -0
  281. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/note/handler.py +0 -0
  282. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/note/repository.py +0 -0
  283. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/note/sqlalchemy_repository.py +0 -0
  284. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/note/use_case.py +0 -0
  285. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/schema.py +0 -0
  286. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/tag/__init__.py +0 -0
  287. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/tag/entity.py +0 -0
  288. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/tag/exceptions.py +0 -0
  289. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/tag/handler.py +0 -0
  290. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/tag/repository.py +0 -0
  291. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/tag/sqlalchemy_repository.py +0 -0
  292. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/example/tag/use_case.py +0 -0
  293. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/__init__.py +0 -0
  294. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/auth/__init__.py +0 -0
  295. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/auth/api_key.py +0 -0
  296. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/auth/bearer_token.py +0 -0
  297. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/auth/deps.py +0 -0
  298. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/auth/exceptions.py +0 -0
  299. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/auth/interfaces.py +0 -0
  300. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/auth/local_verifier.py +0 -0
  301. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/cache/__init__.py +0 -0
  302. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/cache/ttl.py +0 -0
  303. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/config/__init__.py +0 -0
  304. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/config/settings.py +0 -0
  305. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/database/__init__.py +0 -0
  306. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/database/exceptions.py +0 -0
  307. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/database/health.py +0 -0
  308. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/database/interfaces.py +0 -0
  309. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/database/sqlalchemy_executor.py +0 -0
  310. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/database/utils.py +0 -0
  311. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/http/__init__.py +0 -0
  312. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/http/etag.py +0 -0
  313. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/http/health.py +0 -0
  314. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/http/pagination.py +0 -0
  315. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/http/problem_details.py +0 -0
  316. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/log/__init__.py +0 -0
  317. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/log/setup.py +0 -0
  318. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/mcp/__init__.py +0 -0
  319. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/mcp/http_client.py +0 -0
  320. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/mcp/server.py +0 -0
  321. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/middleware/__init__.py +0 -0
  322. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/middleware/domain_exception.py +0 -0
  323. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/middleware/error_handler.py +0 -0
  324. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/middleware/request_id.py +0 -0
  325. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/middleware/request_logging.py +0 -0
  326. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/middleware/request_size_limit.py +0 -0
  327. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/middleware/security_headers.py +0 -0
  328. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/middleware/setup.py +0 -0
  329. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/middleware/throttle.py +0 -0
  330. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/py.typed +0 -0
  331. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/security/__init__.py +0 -0
  332. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/security/webhook.py +0 -0
  333. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/use_case/__init__.py +0 -0
  334. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/use_case/protocols.py +0 -0
  335. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/validation/__init__.py +0 -0
  336. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/nene2/validation/exceptions.py +0 -0
  337. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/scripts/__init__.py +0 -0
  338. {nene2_python-1.8.46 → nene2_python-1.8.47}/src/scripts/export_openapi.py +0 -0
  339. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/__init__.py +0 -0
  340. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/conftest.py +0 -0
  341. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/example/__init__.py +0 -0
  342. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/example/comment/__init__.py +0 -0
  343. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/example/comment/test_comment_http.py +0 -0
  344. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/example/comment/test_comment_repository.py +0 -0
  345. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/example/comment/test_comment_use_case.py +0 -0
  346. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/example/conftest.py +0 -0
  347. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/example/note/__init__.py +0 -0
  348. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/example/note/test_async_note_use_case.py +0 -0
  349. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/example/note/test_list_notes.py +0 -0
  350. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/example/note/test_note_repository.py +0 -0
  351. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/example/tag/__init__.py +0 -0
  352. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/example/tag/test_tag_repository.py +0 -0
  353. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/example/tag/test_tags.py +0 -0
  354. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/example/test_cors.py +0 -0
  355. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/example/test_mcp.py +0 -0
  356. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/__init__.py +0 -0
  357. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/auth/__init__.py +0 -0
  358. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/auth/test_api_key.py +0 -0
  359. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/auth/test_bearer_token.py +0 -0
  360. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/auth/test_make_require_auth.py +0 -0
  361. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/auth/test_token_issuer.py +0 -0
  362. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/cache/__init__.py +0 -0
  363. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/cache/test_ttl.py +0 -0
  364. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/config/__init__.py +0 -0
  365. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/config/test_settings.py +0 -0
  366. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/database/__init__.py +0 -0
  367. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/database/test_transaction.py +0 -0
  368. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/database/test_utils.py +0 -0
  369. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/http/__init__.py +0 -0
  370. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/http/test_etag.py +0 -0
  371. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/http/test_health.py +0 -0
  372. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/http/test_pagination.py +0 -0
  373. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/http/test_problem_details.py +0 -0
  374. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/log/__init__.py +0 -0
  375. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/log/test_setup.py +0 -0
  376. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/mcp/__init__.py +0 -0
  377. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/mcp/test_http_client.py +0 -0
  378. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/mcp/test_server.py +0 -0
  379. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/middleware/__init__.py +0 -0
  380. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/middleware/test_error_handler.py +0 -0
  381. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/middleware/test_request_id.py +0 -0
  382. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/middleware/test_request_logging.py +0 -0
  383. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/middleware/test_request_size_limit.py +0 -0
  384. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/middleware/test_security_headers.py +0 -0
  385. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/middleware/test_setup_middlewares.py +0 -0
  386. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/middleware/test_simple_domain_handler.py +0 -0
  387. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/middleware/test_throttle.py +0 -0
  388. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/security/__init__.py +0 -0
  389. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/security/test_webhook.py +0 -0
  390. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/use_case/__init__.py +0 -0
  391. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/use_case/test_protocols.py +0 -0
  392. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/use_case/test_run_in_threadpool.py +0 -0
  393. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/validation/__init__.py +0 -0
  394. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/nene2/validation/test_exceptions.py +0 -0
  395. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/scripts/__init__.py +0 -0
  396. {nene2_python-1.8.46 → nene2_python-1.8.47}/tests/scripts/test_export_openapi.py +0 -0
  397. {nene2_python-1.8.46 → nene2_python-1.8.47}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nene2-python
3
- Version: 1.8.46
3
+ Version: 1.8.47
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,341 @@
1
+ # FT176: decimal モジュール
2
+
3
+ **日付**: 2026-05-21
4
+ **テーマ**: `decimal.Decimal` による精度の高い十進数演算・金融計算・丸め制御
5
+ **セキュリティ診断**: なし(FT177 で実施)
6
+ **クラッカーペンテスト**: **あり**(FT176: 172 + 4 = 176)
7
+
8
+ ---
9
+
10
+ ## 概要
11
+
12
+ Python 標準ライブラリの `decimal` モジュールを検証する。
13
+ `float` の浮動小数点誤差(`0.1 + 0.2 != 0.3`)を回避し、
14
+ 金融計算で必要な「正確な十進数演算」を `Decimal` 型で実装する。
15
+
16
+ このFTで確認する点:
17
+ - `float` と `Decimal` の精度差(`0.1 + 0.2 == 0.3` の違い)
18
+ - `quantize()` による丸めモードの制御(ROUND_HALF_UP, ROUND_HALF_EVEN 等)
19
+ - 税計算・割引計算・割り勘といった金融計算パターン
20
+ - `Infinity`, `NaN`, 空文字列等の不正入力への防御
21
+ - `parse_decimal_safe()` によるバリデーション(`is_finite()` によるInf/NaN ブロック)
22
+
23
+ ---
24
+
25
+ ## 実装したサンプルアプリ
26
+
27
+ **場所**: `/home/xi/docker/nene2-python-FT/ft176-decimal/`
28
+
29
+ ### 主要機能
30
+
31
+ | 関数/クラス | 概要 |
32
+ |---|---|
33
+ | `decimal_add/sub/mul/div(a, b)` | 基本四則演算(ゼロ除算は `None`) |
34
+ | `round_decimal(value, places, mode)` | 指定モードで丸める |
35
+ | `truncate_decimal(value, places)` | `ROUND_FLOOR` で切り捨て |
36
+ | `ceil_decimal(value, places)` | `ROUND_CEILING` で切り上げ |
37
+ | `ROUNDING_MODES` | 6種の丸めモード辞書 |
38
+ | `calculate_tax(price, tax_rate)` | 税計算(ROUND_HALF_UP) |
39
+ | `calculate_discount(price, discount_percent)` | 割引計算 |
40
+ | `split_bill(total, num_people)` | 割り勘(ROUND_CEILING)|
41
+ | `float_precision_demo()` | float vs Decimal 精度比較 |
42
+ | `parse_decimal_safe(value)` | Infinity/NaN/長すぎる文字列をブロック |
43
+ | `is_valid_decimal(value)` | バリデーション bool |
44
+ | `compare_decimals(a, b)` | 大小比較(-1/0/1) |
45
+
46
+ ### HTTP エンドポイント
47
+
48
+ | メソッド | パス | 概要 |
49
+ |---|---|---|
50
+ | POST | `/decimal/add` | 加算 |
51
+ | POST | `/decimal/sub` | 減算 |
52
+ | POST | `/decimal/mul` | 乗算 |
53
+ | POST | `/decimal/div` | 除算(ゼロ除算 422) |
54
+ | POST | `/decimal/round` | 丸め(truncated, ceiling も返す) |
55
+ | POST | `/decimal/tax` | 税計算 |
56
+ | POST | `/decimal/discount` | 割引計算 |
57
+ | POST | `/decimal/split-bill` | 割り勘 |
58
+ | GET | `/decimal/float-demo` | float 精度比較デモ |
59
+ | GET | `/decimal/validate` | Decimal バリデーション |
60
+ | POST | `/decimal/compare` | 大小比較 |
61
+ | GET | `/decimal/precision` | 現在の計算精度 |
62
+
63
+ ---
64
+
65
+ ## テスト結果
66
+
67
+ **42 passed**
68
+
69
+ ```
70
+ 42 passed in 0.37s
71
+ ```
72
+
73
+ ---
74
+
75
+ ## 摩擦ポイント
76
+
77
+ ### F-1: `ROUND_HALF_EVEN`(銀行家丸め)の挙動が直感と異なる(深刻度: 低)
78
+
79
+ **事象**: `round_decimal("2.5", 0, "ROUND_HALF_EVEN")` → `"2"`(偶数方向)。
80
+ Python の組み込み `round(2.5)` も `2` を返すが(banker's rounding)、
81
+ 多くの現場では「4捨5入」を期待して `ROUND_HALF_UP` を使う。
82
+
83
+ **原因**: `ROUND_HALF_EVEN` は統計的偏りを最小化するため偶数方向に丸める。
84
+ **対応**: ドキュメントに丸めモードの違いを表で説明し、金融計算ではデフォルトを `ROUND_HALF_UP` に設定した。
85
+
86
+ ---
87
+
88
+ ## 観察点
89
+
90
+ ### 観察1: `float` vs `Decimal` の精度差
91
+
92
+ ```python
93
+ 0.1 + 0.2 # 0.30000000000000004
94
+ Decimal("0.1") + Decimal("0.2") # 0.3
95
+
96
+ 0.1 + 0.2 == 0.3 # False
97
+ Decimal("0.1") + Decimal("0.2") == Decimal("0.3") # True
98
+ ```
99
+
100
+ `Decimal` は文字列から初期化する必要がある。`Decimal(0.1)` は float の誤差を引き継ぐ。
101
+
102
+ ### 観察2: `quantize()` による金融計算の標準パターン
103
+
104
+ ```python
105
+ tax = (price * rate).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
106
+ ```
107
+
108
+ `quantize(Decimal("0.01"))` が「小数点以下2桁」を指定する慣用表現。
109
+ `Decimal(10) ** -2` と等価。`quantize` なしで演算すると桁数が増加する。
110
+
111
+ ### 観察3: `Decimal("Infinity")` と `is_finite()` の組み合わせ
112
+
113
+ ```python
114
+ def parse_decimal_safe(value: str) -> Decimal | None:
115
+ result = Decimal(value)
116
+ if not result.is_finite(): # Infinity / -Infinity / NaN を拒否
117
+ return None
118
+ return result
119
+ ```
120
+
121
+ `Decimal("Infinity")`, `Decimal("NaN")`, `Decimal("sNaN")` は `InvalidOperation` を
122
+ 投げずに正常に生成される。`is_finite()` チェックが必要な理由がここにある。
123
+
124
+ ### 観察4: `split_bill` の ROUND_CEILING で全員が必ず払える金額に
125
+
126
+ ```python
127
+ # 1000 / 3 = 333.333...
128
+ # ROUND_CEILING で 333.34 に切り上げ → 全員が333.34払うと 1000.02 になるが
129
+ # これは「端数は最初の人が多く払う」設計ではなく「全員同額で超えたら少し多い」設計
130
+ per_person = (total / num_people).quantize(Decimal("0.01"), rounding=ROUND_CEILING)
131
+ ```
132
+
133
+ ---
134
+
135
+ ## nene2-python フレームワークとの統合
136
+
137
+ - `BinaryOpBody`, `RoundBody`, `TaxBody` 等の Pydantic モデルで `max_length=30` を設定
138
+ - `_validate_decimal()` ヘルパーが `parse_decimal_safe()` を呼び出し、不正入力に一貫した 422 を返す
139
+ - セキュリティヘッダーとリクエストIDが全レスポンスに付与されている
140
+
141
+ ---
142
+
143
+ ## Developer Experience (DX) Review
144
+
145
+ ### ペルソナ1: 初心者(Python 歴1年・独学中・女性・バックエンド志望)
146
+
147
+ `Decimal` のコンストラクターに **文字列**を渡す必要がある点は最初のつまずき。
148
+
149
+ **ドキュメント理解**: `Decimal("0.1")` vs `Decimal(0.1)` の違いを説明する必要がある。
150
+ **事故リスク**: 高。`Decimal(0.1)` で float 誤差を引き継ぐコードを書きがち。
151
+ **規約の使いやすさ**: `parse_decimal_safe()` のファクトリ関数パターンは使いやすい。
152
+
153
+ ### ペルソナ2: ロースキル経験者(Python 歴3-4年・スクリプト系・男性・SES)
154
+
155
+ 既存コードの `float` を `Decimal` に置換するとき `str()` 経由が必要なことを知らない。
156
+
157
+ **コピペ可能性**: `calculate_tax()` パターンはそのままコピーして使える。
158
+ **拡張時の罠**: `quantize()` の `places` と `Decimal("0.01")` の関係が初見でわかりにくい。
159
+ **セキュリティ的な事故リスク**: 中。負の価格・税率のバリデーション欠如(ペンテストで発見)。
160
+
161
+ ### ペルソナ3: フロントエンド寄り経験者(React/TS 歴4年・バックエンド転向中・ノンバイナリ)
162
+
163
+ JS の `number` 型が浮動小数点演算なので、バックエンドが `Decimal` で正確に計算することの重要性を理解できる。
164
+
165
+ **エラーレスポンスの質**: 422 に `field_name` が含まれるため、フロント側のフォームバリデーションと対応しやすい。
166
+ **Python 固有概念の学習コスト**: `quantize` は JS には直接対応物がないが、「N桁に揃える」と説明すればわかる。
167
+ **事故リスク**: 低。HTTP 境界で `max_length` と `is_finite()` が守っている。
168
+
169
+ ### ペルソナ4: バックエンド経験者(Django/FastAPI 歴5-6年・男性・リードエンジニア)
170
+
171
+ 金融システムで `Decimal` は必須。`quantize(ROUND_HALF_UP)` パターンを見れば即理解できる。
172
+
173
+ **他フレームワークとの差異**: Django の `DecimalField` はモデル側で `decimal_places` を指定するが、
174
+ このFTでは演算ごとに `quantize()` を呼ぶ明示的スタイル。どちらも正しい。
175
+ **nene2-python の薄さへの評価**: ドメインロジック(金融計算)が HTTP 層から完全に独立している点が評価できる。
176
+ **本番投入可能性**: ビジネスロジックバリデーション(範囲チェック)を追加すれば本番品質。
177
+
178
+ ### ペルソナ5: シニアエンジニア(設計・コードレビュー担当・女性・10-12年)
179
+
180
+ ペンテストで発見されたビジネスロジック欠如(負の価格・100%超割引)は Issue 化が必要。
181
+
182
+ **コードレビューチェックポイント**:
183
+ - [x] `Infinity`, `NaN` が `is_finite()` でブロックされているか — OK
184
+ - [x] ゼロ除算が安全に処理されているか — `decimal_div()` で None 返却 ✅
185
+ - [ ] `calculate_tax()` の `tax_rate` に範囲制限がない — `0 <= tax_rate <= 2` 程度のバリデーションが必要
186
+ - [ ] `calculate_discount()` の `discount_percent` が 0〜100 のチェックがない
187
+ - [ ] Unicode 全角数字(`'123'`)が通過する — Decimal コンストラクターが Unicode digit を受け入れる
188
+
189
+ **チームでの安全な共有パターン**: `parse_decimal_safe()` を必ず経由するルールをチーム内で徹底することが必要。
190
+ **ツール追加の必要性**: ruff には `Decimal(float)` を禁止するルールがないため、コードレビューで手動確認が必要。
191
+
192
+ ### ペルソナ6: 設計者・ポリシー照合(nene2-python 設計ポリシー目線)
193
+
194
+ CLAUDE.md の「数値フィールドに `ge` / `le` / `gt` / `lt` 範囲制限があるか」ポリシーに対して、
195
+ `Decimal` 型は文字列で受け取るため Pydantic の数値制限が適用されない点が設計的な空白。
196
+
197
+ **ポリシー達成度**: 中(Pydantic で数値範囲制限できない文字列 Decimal の扱いが未定義)
198
+ **「初心者でも安全な API」達成度**: 中(Infinity/NaN は守られているがビジネスルール違反は通過)
199
+ **設計上の負債**: 文字列 Decimal の範囲バリデーションパターンをフレームワークに追加する必要がある
200
+ **Follow-up Issue 候補**: `Pydantic Annotated` で `DecimalStr` 型エイリアスを定義して範囲制限を組み込む
201
+
202
+ ---
203
+
204
+ ## クラッカーペンテスト(FT176: 172 + 4 = 176)
205
+
206
+ > **実施方針**: 金融計算 API は「数値の正確さ」と「ビジネスロジックの整合性」の両面から攻撃できる。
207
+ > クラッカーは価格をマイナスにして不正な返金を引き出したり、税率を異常値にして計算を崩したりする。
208
+
209
+ ### フェーズ1: 構造推測(攻撃者の視点)
210
+
211
+ - **OpenAPI から推測できる内部構造**:
212
+ - 全フィールドが `str` 型 → `Decimal(str)` を内部で使っていると推測
213
+ - `max_length=30` → 入力サイズ制限が文字数ベース(桁数ではない)
214
+ - `num_people: int` に `ge=1, le=1000` → Pydantic 数値制限あり
215
+ - `price`, `tax_rate` に数値範囲制限なし → バリデーション欠如の可能性
216
+
217
+ - **攻撃ベクターの仮説**:
218
+ 1. `Infinity`, `NaN` を渡してランタイムエラーを引き起こす
219
+ 2. 負の価格・100%超の税率でビジネスロジックを崩す
220
+ 3. Unicode 文字を数値として送り込む
221
+ 4. 科学表記(`1e100`)で予期しない巨大数を計算させる
222
+ 5. 精度の高い計算を大量に送ってCPUを枯渇させる
223
+
224
+ ### フェーズ2: 攻撃実行ログ
225
+
226
+ #### A. Pydantic バイパス・型強制攻撃
227
+
228
+ ```
229
+ a='Infinity': 422 Invalid decimal: a='Infinity' ← ブロック ✅
230
+ a='-Infinity': 422 ← ブロック ✅
231
+ a='NaN': 422 ← ブロック ✅
232
+ a='sNaN': 422 ← ブロック ✅
233
+ a='inf': 422 ← ブロック ✅
234
+ a='-inf': 422 ← ブロック ✅
235
+ a='nan': 422 ← ブロック ✅
236
+
237
+ a='1e10': 200 result=10000000000 ← 通過(有限値として正当)
238
+ a='1E308': 200 result=1.000...E+308 ← 通過(有限値として正当)
239
+ a='1e100': 200 result=1.000...E+100 ← 通過(有限値として正当)
240
+
241
+ a=123 (int type): 422 string_type error ← Pydantic が str を要求 ✅
242
+ ```
243
+
244
+ **結果**: Infinity/NaN は全7種類ブロック。科学表記は有限値として通過(許容動作)。
245
+
246
+ #### B. ビジネスロジック攻撃
247
+
248
+ ```
249
+ tax_rate=2.0 (200%超): 200 tax=2000.00, total=3000.00 ← 突破 ⚠️
250
+ price=-1000 (負の価格): 200 tax=-100.00, total=-1100.00 ← 突破 ⚠️
251
+ discount_percent=-10 (負割引): 200 → 値上がり ← 突破 ⚠️
252
+ discount_percent=150 (100%超): 200 discounted=-500.00 ← 突破 ⚠️
253
+ div by zero: 422 ← ブロック ✅
254
+ ```
255
+
256
+ **結果**: 数値範囲バリデーションが未実装のため、負の価格・異常税率が通過。
257
+ 金融 API として使う場合はビジネスロジックレベルの制約が必要。
258
+
259
+ #### C. 境界値・エッジケース攻撃
260
+
261
+ ```
262
+ a="" (空文字): 422 Invalid decimal ← ブロック ✅
263
+ len=30 (上限ちょうど): 200 ← 通過(正常) ✅
264
+ len=31 (上限超え): 422 string_too_long ← Pydantic ブロック ✅
265
+ 28桁 all-9s + 1: 200 result=1E+28 ← 通過(正常) ✅
266
+ 全角数字 '123' + 1: 200 result='124' ← 通過(予期しない動作)⚠️
267
+ ```
268
+
269
+ **発見**: Python の `Decimal` コンストラクターは Unicode の全角数字(`'123'`)を受け付け、
270
+ `123` と同じ値として扱う。`parse_decimal_safe()` は `InvalidOperation` が発生しないため通過する。
271
+ 金融 API でユーザーが全角数字を入力した場合、期待通りに動作するが、
272
+ 入力形式の正規化なしに通過することに開発者が気づいていない可能性がある。
273
+
274
+ #### D. 情報収集攻撃
275
+
276
+ ```
277
+ Invalid mode 'HACKED': 422 Unknown rounding mode: 'HACKED' ← 安全なエラーメッセージ ✅
278
+ 不正入力のエラー: 内部パス・スタックトレースなし ✅
279
+ ```
280
+
281
+ **結果**: エラーメッセージは適切に制御されている。
282
+
283
+ #### E. DoS 試み
284
+
285
+ ```
286
+ 100回 div(1/3): 0.321s (3.2ms/req) ← 正常速度 ✅
287
+ 50回 mul(28桁×28桁): 0.163s (3.3ms/req) ← 正常速度 ✅
288
+ 攻撃後の精度: 28 (不変) ← グローバル状態汚染なし ✅
289
+ ```
290
+
291
+ **結果**: `1e100 + 1e100` のような巨大数演算も正常速度。
292
+ `decimal.getcontext().prec` はスレッドローカルなため、攻撃による変更は他スレッドに影響しない。
293
+
294
+ ### フェーズ3: 攻撃まとめ
295
+
296
+ | 攻撃カテゴリ | 試みた攻撃数 | 突破 | 耐えた | 予期しない動作 |
297
+ |---|---|---|---|---|
298
+ | Pydantic バイパス(Inf/NaN) | 7 | 0 | 7 | 0 |
299
+ | 型強制(int型フィールド) | 1 | 0 | 1 | 0 |
300
+ | ビジネスロジック(範囲) | 4 | 4 | 0 | 0 |
301
+ | 境界値(長さ・文字種) | 5 | 0 | 4 | 1 |
302
+ | 情報収集(エラー解析) | 2 | 0 | 2 | 0 |
303
+ | DoS(大量・高精度計算) | 3 | 0 | 3 | 0 |
304
+
305
+ **攻撃耐性評価**: 軽微な問題あり(ビジネスロジックバリデーション欠如)
306
+
307
+ **発見した弱点**:
308
+ 1. **MEDIUM**: `calculate_tax()`, `calculate_discount()` に価格・税率・割引率の範囲制限なし
309
+ - 負の価格(`-1000`)→ 負の税額
310
+ - 200%の税率(`2.0`)→ 元価格の3倍
311
+ - 150%の割引(`150`)→ マイナス価格
312
+
313
+ 2. **LOW**: Unicode 全角数字(`'123'`)が `Decimal` に通過する
314
+ - `parse_decimal_safe` は `is_finite()` で判定するが、Unicode digit は有限値なので通過
315
+ - 機能的には正しく動作するが、予期しない入力形式
316
+
317
+ ---
318
+
319
+ ## Follow-up Issues
320
+
321
+ | 優先度 | タイトル | 種別 |
322
+ |---|---|---|
323
+ | 高 | `calculate_tax()`, `calculate_discount()` に価格・税率・割引率の範囲バリデーションを追加 | fix |
324
+ | 中 | `parse_decimal_safe()` に ASCII 数字のみ許可するオプションを追加(Unicode digit の予期しない受け入れを防ぐ) | feat |
325
+ | 中 | 文字列 Decimal の `Annotated` 型エイリアス(`PositiveDecimalStr`, `TaxRateStr`)をフレームワークに追加 | feat |
326
+ | 低 | `Decimal(0.1)` を禁止するカスタム ruff ルールの検討 | chore |
327
+
328
+ ---
329
+
330
+ ## まとめ
331
+
332
+ FT176 では `decimal.Decimal` による精度の高い金融計算を実装した。
333
+ `float` との精度差(`0.1 + 0.2 != 0.3` 問題)、`quantize()` による丸め制御、
334
+ `is_finite()` による `Infinity`/`NaN` ブロックを確認した。
335
+
336
+ クラッカーペンテストでは Infinity/NaN の全種類が正常にブロックされたが、
337
+ ビジネスロジックレベルのバリデーション(負の価格・100%超の税率)が欠如していることを発見した。
338
+ 金融 API では「計算として正しい値」と「ビジネスとして許容できる値」の区別が重要で、
339
+ `parse_decimal_safe()` の技術的バリデーションだけでは不十分であることが確認された。
340
+
341
+ 次の FT177 は 177 % 3 = 0 → セキュリティ診断が必要。
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nene2-python"
3
- version = "1.8.46"
3
+ version = "1.8.47"
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