nene2-python 1.8.48__tar.gz → 1.8.49__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 (401) hide show
  1. {nene2_python-1.8.48 → nene2_python-1.8.49}/PKG-INFO +1 -1
  2. nene2_python-1.8.49/docs/field-trials/2026-05-field-trial-178.md +242 -0
  3. nene2_python-1.8.49/docs/field-trials/INDEX.md +229 -0
  4. nene2_python-1.8.49/docs/todo/current.md +63 -0
  5. {nene2_python-1.8.48 → nene2_python-1.8.49}/pyproject.toml +2 -1
  6. nene2_python-1.8.48/docs/todo/current.md +0 -52
  7. {nene2_python-1.8.48 → nene2_python-1.8.49}/.env.example +0 -0
  8. {nene2_python-1.8.48 → nene2_python-1.8.49}/.github/workflows/ci.yml +0 -0
  9. {nene2_python-1.8.48 → nene2_python-1.8.49}/.github/workflows/docs.yml +0 -0
  10. {nene2_python-1.8.48 → nene2_python-1.8.49}/.github/workflows/publish.yml +0 -0
  11. {nene2_python-1.8.48 → nene2_python-1.8.49}/.gitignore +0 -0
  12. {nene2_python-1.8.48 → nene2_python-1.8.49}/.vitepress/config.mts +0 -0
  13. {nene2_python-1.8.48 → nene2_python-1.8.49}/.vitepress/theme/custom.css +0 -0
  14. {nene2_python-1.8.48 → nene2_python-1.8.49}/.vitepress/theme/index.ts +0 -0
  15. {nene2_python-1.8.48 → nene2_python-1.8.49}/AGENTS.md +0 -0
  16. {nene2_python-1.8.48 → nene2_python-1.8.49}/CHANGELOG.md +0 -0
  17. {nene2_python-1.8.48 → nene2_python-1.8.49}/CLAUDE.md +0 -0
  18. {nene2_python-1.8.48 → nene2_python-1.8.49}/Dockerfile +0 -0
  19. {nene2_python-1.8.48 → nene2_python-1.8.49}/LICENSE +0 -0
  20. {nene2_python-1.8.48 → nene2_python-1.8.49}/README.md +0 -0
  21. {nene2_python-1.8.48 → nene2_python-1.8.49}/alembic/README +0 -0
  22. {nene2_python-1.8.48 → nene2_python-1.8.49}/alembic/env.py +0 -0
  23. {nene2_python-1.8.48 → nene2_python-1.8.49}/alembic/script.py.mako +0 -0
  24. {nene2_python-1.8.48 → nene2_python-1.8.49}/alembic/versions/001_create_notes_and_tags_tables.py +0 -0
  25. {nene2_python-1.8.48 → nene2_python-1.8.49}/alembic.ini +0 -0
  26. {nene2_python-1.8.48 → nene2_python-1.8.49}/compose.yaml +0 -0
  27. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/adr/0001-toolchain.md +0 -0
  28. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/adr/0002-clean-architecture.md +0 -0
  29. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/adr/0003-security-first.md +0 -0
  30. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/adr/0004-ai-first-design.md +0 -0
  31. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/adr/0005-logging.md +0 -0
  32. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/adr/0006-rate-limiting.md +0 -0
  33. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/adr/0009-mcp-design.md +0 -0
  34. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/adr/0010-async-use-case.md +0 -0
  35. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/adr/0011-mcp-as-core-dependency.md +0 -0
  36. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/de/index.md +0 -0
  37. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/de/tutorials/getting-started.md +0 -0
  38. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/explanation/architecture.md +0 -0
  39. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/explanation/design-philosophy.md +0 -0
  40. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-1.md +0 -0
  41. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-10.md +0 -0
  42. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-100.md +0 -0
  43. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-101.md +0 -0
  44. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-102.md +0 -0
  45. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-103.md +0 -0
  46. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-104.md +0 -0
  47. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-105.md +0 -0
  48. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-106.md +0 -0
  49. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-107.md +0 -0
  50. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-108.md +0 -0
  51. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-109.md +0 -0
  52. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-11.md +0 -0
  53. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-110.md +0 -0
  54. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-111.md +0 -0
  55. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-112.md +0 -0
  56. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-113.md +0 -0
  57. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-114.md +0 -0
  58. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-115.md +0 -0
  59. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-116.md +0 -0
  60. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-117.md +0 -0
  61. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-118.md +0 -0
  62. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-119.md +0 -0
  63. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-12.md +0 -0
  64. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-120.md +0 -0
  65. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-121.md +0 -0
  66. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-122.md +0 -0
  67. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-123.md +0 -0
  68. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-124.md +0 -0
  69. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-125.md +0 -0
  70. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-126.md +0 -0
  71. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-127.md +0 -0
  72. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-128.md +0 -0
  73. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-129.md +0 -0
  74. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-13.md +0 -0
  75. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-130.md +0 -0
  76. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-131.md +0 -0
  77. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-132.md +0 -0
  78. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-133.md +0 -0
  79. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-134.md +0 -0
  80. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-135.md +0 -0
  81. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-136.md +0 -0
  82. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-137.md +0 -0
  83. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-138.md +0 -0
  84. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-139.md +0 -0
  85. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-14.md +0 -0
  86. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-140.md +0 -0
  87. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-141.md +0 -0
  88. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-142.md +0 -0
  89. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-143.md +0 -0
  90. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-144.md +0 -0
  91. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-145.md +0 -0
  92. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-146.md +0 -0
  93. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-147.md +0 -0
  94. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-148.md +0 -0
  95. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-149.md +0 -0
  96. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-15.md +0 -0
  97. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-150.md +0 -0
  98. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-151.md +0 -0
  99. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-152.md +0 -0
  100. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-153.md +0 -0
  101. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-154.md +0 -0
  102. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-155.md +0 -0
  103. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-156.md +0 -0
  104. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-157.md +0 -0
  105. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-158.md +0 -0
  106. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-159.md +0 -0
  107. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-16.md +0 -0
  108. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-160.md +0 -0
  109. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-161.md +0 -0
  110. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-162.md +0 -0
  111. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-163.md +0 -0
  112. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-164.md +0 -0
  113. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-165.md +0 -0
  114. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-166.md +0 -0
  115. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-167.md +0 -0
  116. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-168.md +0 -0
  117. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-169.md +0 -0
  118. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-17.md +0 -0
  119. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-170.md +0 -0
  120. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-171.md +0 -0
  121. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-172.md +0 -0
  122. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-173.md +0 -0
  123. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-174.md +0 -0
  124. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-175.md +0 -0
  125. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-176.md +0 -0
  126. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-177.md +0 -0
  127. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-18.md +0 -0
  128. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-19.md +0 -0
  129. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-2.md +0 -0
  130. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-20.md +0 -0
  131. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-21.md +0 -0
  132. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-22.md +0 -0
  133. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-23.md +0 -0
  134. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-24.md +0 -0
  135. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-25.md +0 -0
  136. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-26.md +0 -0
  137. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-27.md +0 -0
  138. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-28.md +0 -0
  139. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-29.md +0 -0
  140. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-3.md +0 -0
  141. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-30.md +0 -0
  142. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-31.md +0 -0
  143. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-32.md +0 -0
  144. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-33.md +0 -0
  145. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-34.md +0 -0
  146. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-35.md +0 -0
  147. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-36.md +0 -0
  148. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-37.md +0 -0
  149. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-38.md +0 -0
  150. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-39.md +0 -0
  151. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-4.md +0 -0
  152. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-40.md +0 -0
  153. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-41.md +0 -0
  154. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-42.md +0 -0
  155. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-43.md +0 -0
  156. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-44.md +0 -0
  157. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-45.md +0 -0
  158. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-46.md +0 -0
  159. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-47.md +0 -0
  160. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-48.md +0 -0
  161. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-49.md +0 -0
  162. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-5.md +0 -0
  163. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-50.md +0 -0
  164. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-51.md +0 -0
  165. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-52.md +0 -0
  166. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-53.md +0 -0
  167. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-54.md +0 -0
  168. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-55.md +0 -0
  169. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-56.md +0 -0
  170. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-57.md +0 -0
  171. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-58.md +0 -0
  172. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-59.md +0 -0
  173. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-6.md +0 -0
  174. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-60.md +0 -0
  175. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-61.md +0 -0
  176. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-62.md +0 -0
  177. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-63.md +0 -0
  178. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-64.md +0 -0
  179. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-65.md +0 -0
  180. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-66.md +0 -0
  181. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-67.md +0 -0
  182. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-68.md +0 -0
  183. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-69.md +0 -0
  184. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-7.md +0 -0
  185. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-70.md +0 -0
  186. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-71.md +0 -0
  187. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-72.md +0 -0
  188. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-73.md +0 -0
  189. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-74.md +0 -0
  190. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-75.md +0 -0
  191. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-76.md +0 -0
  192. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-77.md +0 -0
  193. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-78.md +0 -0
  194. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-79.md +0 -0
  195. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-8.md +0 -0
  196. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-80.md +0 -0
  197. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-81.md +0 -0
  198. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-82.md +0 -0
  199. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-83.md +0 -0
  200. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-84.md +0 -0
  201. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-85.md +0 -0
  202. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-86.md +0 -0
  203. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-87.md +0 -0
  204. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-88.md +0 -0
  205. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-89.md +0 -0
  206. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-9.md +0 -0
  207. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-90.md +0 -0
  208. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-91.md +0 -0
  209. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-92.md +0 -0
  210. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-93.md +0 -0
  211. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-94.md +0 -0
  212. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-95.md +0 -0
  213. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-96.md +0 -0
  214. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-97.md +0 -0
  215. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-98.md +0 -0
  216. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/field-trials/2026-05-field-trial-99.md +0 -0
  217. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/fr/index.md +0 -0
  218. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/fr/tutorials/getting-started.md +0 -0
  219. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/how-to/add-new-domain.md +0 -0
  220. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/how-to/api-versioning.md +0 -0
  221. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/how-to/async-use-case.md +0 -0
  222. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/how-to/background-tasks.md +0 -0
  223. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/how-to/configure-auth.md +0 -0
  224. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/how-to/cors.md +0 -0
  225. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/how-to/custom-auth-middleware.md +0 -0
  226. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/how-to/dependency-injection.md +0 -0
  227. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/how-to/domain-events.md +0 -0
  228. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/how-to/file-upload.md +0 -0
  229. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/how-to/lifespan-and-app-state.md +0 -0
  230. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/how-to/middleware-stack.md +0 -0
  231. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/how-to/new-project.md +0 -0
  232. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/how-to/problem-details.md +0 -0
  233. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/how-to/response-patterns.md +0 -0
  234. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/how-to/run-tests.md +0 -0
  235. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/how-to/soft-delete.md +0 -0
  236. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/how-to/sqlalchemy-repository.md +0 -0
  237. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/how-to/streaming.md +0 -0
  238. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/how-to/structured-logging.md +0 -0
  239. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/how-to/validation.md +0 -0
  240. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/how-to/webhook.md +0 -0
  241. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/howto/mcp-setup.md +0 -0
  242. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/index.md +0 -0
  243. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/ja/explanation/architecture.md +0 -0
  244. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/ja/explanation/design-philosophy.md +0 -0
  245. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/ja/how-to/add-new-domain.md +0 -0
  246. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/ja/how-to/configure-auth.md +0 -0
  247. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/ja/how-to/new-project.md +0 -0
  248. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/ja/how-to/run-tests.md +0 -0
  249. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/ja/how-to/sqlalchemy-repository.md +0 -0
  250. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/ja/howto/mcp-setup.md +0 -0
  251. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/ja/index.md +0 -0
  252. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/ja/reference/api.md +0 -0
  253. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/ja/reference/configuration.md +0 -0
  254. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/ja/reference/framework-modules.md +0 -0
  255. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/ja/tutorials/first-domain.md +0 -0
  256. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/ja/tutorials/getting-started.md +0 -0
  257. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/pt-br/index.md +0 -0
  258. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/pt-br/tutorials/getting-started.md +0 -0
  259. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/reference/api.md +0 -0
  260. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/reference/configuration.md +0 -0
  261. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/reference/framework-modules.md +0 -0
  262. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/roadmap.md +0 -0
  263. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/templates/field-trial-report.md +0 -0
  264. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/tutorials/first-domain.md +0 -0
  265. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/tutorials/getting-started.md +0 -0
  266. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/zh/index.md +0 -0
  267. {nene2_python-1.8.48 → nene2_python-1.8.49}/docs/zh/tutorials/getting-started.md +0 -0
  268. {nene2_python-1.8.48 → nene2_python-1.8.49}/package-lock.json +0 -0
  269. {nene2_python-1.8.48 → nene2_python-1.8.49}/package.json +0 -0
  270. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/__init__.py +0 -0
  271. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/__main__.py +0 -0
  272. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/app.py +0 -0
  273. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/comment/__init__.py +0 -0
  274. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/comment/entity.py +0 -0
  275. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/comment/exceptions.py +0 -0
  276. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/comment/handler.py +0 -0
  277. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/comment/repository.py +0 -0
  278. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/comment/sqlalchemy_repository.py +0 -0
  279. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/comment/use_case.py +0 -0
  280. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/mcp.py +0 -0
  281. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/note/__init__.py +0 -0
  282. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/note/async_use_case.py +0 -0
  283. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/note/entity.py +0 -0
  284. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/note/exceptions.py +0 -0
  285. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/note/handler.py +0 -0
  286. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/note/repository.py +0 -0
  287. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/note/sqlalchemy_repository.py +0 -0
  288. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/note/use_case.py +0 -0
  289. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/schema.py +0 -0
  290. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/tag/__init__.py +0 -0
  291. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/tag/entity.py +0 -0
  292. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/tag/exceptions.py +0 -0
  293. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/tag/handler.py +0 -0
  294. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/tag/repository.py +0 -0
  295. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/tag/sqlalchemy_repository.py +0 -0
  296. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/example/tag/use_case.py +0 -0
  297. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/__init__.py +0 -0
  298. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/auth/__init__.py +0 -0
  299. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/auth/api_key.py +0 -0
  300. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/auth/bearer_token.py +0 -0
  301. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/auth/deps.py +0 -0
  302. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/auth/exceptions.py +0 -0
  303. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/auth/interfaces.py +0 -0
  304. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/auth/local_verifier.py +0 -0
  305. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/cache/__init__.py +0 -0
  306. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/cache/ttl.py +0 -0
  307. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/config/__init__.py +0 -0
  308. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/config/settings.py +0 -0
  309. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/database/__init__.py +0 -0
  310. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/database/exceptions.py +0 -0
  311. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/database/health.py +0 -0
  312. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/database/interfaces.py +0 -0
  313. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/database/sqlalchemy_executor.py +0 -0
  314. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/database/utils.py +0 -0
  315. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/http/__init__.py +0 -0
  316. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/http/etag.py +0 -0
  317. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/http/health.py +0 -0
  318. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/http/pagination.py +0 -0
  319. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/http/problem_details.py +0 -0
  320. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/log/__init__.py +0 -0
  321. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/log/setup.py +0 -0
  322. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/mcp/__init__.py +0 -0
  323. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/mcp/http_client.py +0 -0
  324. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/mcp/server.py +0 -0
  325. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/middleware/__init__.py +0 -0
  326. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/middleware/domain_exception.py +0 -0
  327. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/middleware/error_handler.py +0 -0
  328. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/middleware/request_id.py +0 -0
  329. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/middleware/request_logging.py +0 -0
  330. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/middleware/request_size_limit.py +0 -0
  331. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/middleware/security_headers.py +0 -0
  332. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/middleware/setup.py +0 -0
  333. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/middleware/throttle.py +0 -0
  334. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/py.typed +0 -0
  335. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/security/__init__.py +0 -0
  336. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/security/webhook.py +0 -0
  337. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/use_case/__init__.py +0 -0
  338. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/use_case/protocols.py +0 -0
  339. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/validation/__init__.py +0 -0
  340. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/nene2/validation/exceptions.py +0 -0
  341. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/scripts/__init__.py +0 -0
  342. {nene2_python-1.8.48 → nene2_python-1.8.49}/src/scripts/export_openapi.py +0 -0
  343. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/__init__.py +0 -0
  344. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/conftest.py +0 -0
  345. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/example/__init__.py +0 -0
  346. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/example/comment/__init__.py +0 -0
  347. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/example/comment/test_comment_http.py +0 -0
  348. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/example/comment/test_comment_repository.py +0 -0
  349. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/example/comment/test_comment_use_case.py +0 -0
  350. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/example/conftest.py +0 -0
  351. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/example/note/__init__.py +0 -0
  352. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/example/note/test_async_note_use_case.py +0 -0
  353. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/example/note/test_list_notes.py +0 -0
  354. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/example/note/test_note_repository.py +0 -0
  355. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/example/tag/__init__.py +0 -0
  356. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/example/tag/test_tag_repository.py +0 -0
  357. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/example/tag/test_tags.py +0 -0
  358. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/example/test_cors.py +0 -0
  359. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/example/test_mcp.py +0 -0
  360. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/__init__.py +0 -0
  361. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/auth/__init__.py +0 -0
  362. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/auth/test_api_key.py +0 -0
  363. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/auth/test_bearer_token.py +0 -0
  364. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/auth/test_make_require_auth.py +0 -0
  365. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/auth/test_token_issuer.py +0 -0
  366. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/cache/__init__.py +0 -0
  367. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/cache/test_ttl.py +0 -0
  368. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/config/__init__.py +0 -0
  369. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/config/test_settings.py +0 -0
  370. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/database/__init__.py +0 -0
  371. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/database/test_transaction.py +0 -0
  372. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/database/test_utils.py +0 -0
  373. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/http/__init__.py +0 -0
  374. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/http/test_etag.py +0 -0
  375. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/http/test_health.py +0 -0
  376. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/http/test_pagination.py +0 -0
  377. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/http/test_problem_details.py +0 -0
  378. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/log/__init__.py +0 -0
  379. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/log/test_setup.py +0 -0
  380. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/mcp/__init__.py +0 -0
  381. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/mcp/test_http_client.py +0 -0
  382. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/mcp/test_server.py +0 -0
  383. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/middleware/__init__.py +0 -0
  384. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/middleware/test_error_handler.py +0 -0
  385. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/middleware/test_request_id.py +0 -0
  386. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/middleware/test_request_logging.py +0 -0
  387. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/middleware/test_request_size_limit.py +0 -0
  388. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/middleware/test_security_headers.py +0 -0
  389. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/middleware/test_setup_middlewares.py +0 -0
  390. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/middleware/test_simple_domain_handler.py +0 -0
  391. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/middleware/test_throttle.py +0 -0
  392. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/security/__init__.py +0 -0
  393. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/security/test_webhook.py +0 -0
  394. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/use_case/__init__.py +0 -0
  395. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/use_case/test_protocols.py +0 -0
  396. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/use_case/test_run_in_threadpool.py +0 -0
  397. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/validation/__init__.py +0 -0
  398. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/nene2/validation/test_exceptions.py +0 -0
  399. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/scripts/__init__.py +0 -0
  400. {nene2_python-1.8.48 → nene2_python-1.8.49}/tests/scripts/test_export_openapi.py +0 -0
  401. {nene2_python-1.8.48 → nene2_python-1.8.49}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nene2-python
3
- Version: 1.8.48
3
+ Version: 1.8.49
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,242 @@
1
+ # FT178: base64 モジュール
2
+
3
+ **日付**: 2026-05-21
4
+ **テーマ**: エンコード・デコード・URL セーフ変換・データ URI・HTTP Basic Auth パース
5
+ **セキュリティ診断**: なし(178 % 3 = 1)
6
+
7
+ ---
8
+
9
+ ## 概要
10
+
11
+ Web API で頻繁に使う `base64` モジュールを検証する。
12
+ 標準エンコーディングと URL セーフ変換の違い、パディング扱い、データ URI 生成、
13
+ HTTP Basic Auth ヘッダーのパースまで網羅し、落とし穴となる箇所を洗い出す。
14
+
15
+ ---
16
+
17
+ ## 実装したサンプルアプリ
18
+
19
+ **場所**: `/home/xi/docker/nene2-python-FT/ft178-base64/`
20
+
21
+ ### 主要機能
22
+
23
+ | 関数 | 概要 |
24
+ |---|---|
25
+ | `encode_standard(data)` | 標準 base64 エンコード(RFC 4648 §4) |
26
+ | `decode_standard(s)` | 標準 base64 デコード(`validate=True` で厳格検証) |
27
+ | `encode_url_safe(data)` | URL セーフ base64(パディングなし) |
28
+ | `decode_url_safe(s)` | URL セーフ base64 デコード(パディング自動補完 + 文字セット検証) |
29
+ | `encode_text(text)` | UTF-8 テキスト → base64 |
30
+ | `decode_text(s)` | base64 → UTF-8 テキスト(非 UTF-8 は `None`) |
31
+ | `is_valid_base64(s)` | 長さ・文字セット・パディングを検証 |
32
+ | `is_valid_url_safe_base64(s)` | URL セーフ文字セット検証 |
33
+ | `make_data_uri(content, mime_type)` | RFC 2397 データ URI 生成 |
34
+ | `parse_data_uri(uri)` | データ URI → `(mime_type, bytes)` |
35
+ | `encode_basic_auth(username, password)` | HTTP Basic Auth ヘッダー値生成 |
36
+ | `parse_basic_auth(header)` | Authorization ヘッダー → `(username, password)` |
37
+
38
+ ### HTTP エンドポイント
39
+
40
+ | メソッド | パス | 概要 |
41
+ |---|---|---|
42
+ | POST | `/encode` | 標準 base64 エンコード |
43
+ | POST | `/decode` | 標準 base64 デコード |
44
+ | POST | `/encode/url-safe` | URL セーフ base64 エンコード |
45
+ | POST | `/decode/url-safe` | URL セーフ base64 デコード |
46
+ | POST | `/encode/text` | テキスト → base64 |
47
+ | POST | `/decode/text` | base64 → テキスト |
48
+ | POST | `/data-uri/encode` | データ URI 生成 |
49
+ | POST | `/data-uri/parse` | データ URI 解析 |
50
+ | POST | `/auth/basic/encode` | Basic Auth ヘッダー生成 |
51
+ | POST | `/auth/basic/parse` | Basic Auth ヘッダー解析 |
52
+
53
+ ---
54
+
55
+ ## テスト結果
56
+
57
+ **58 passed**(初回 1 失敗 → 修正後 58 全通過)
58
+
59
+ ```
60
+ 58 passed in 0.43s
61
+ ```
62
+
63
+ mypy: Success / ruff: All checks passed / pip-audit: PYSEC-2025-183(継続監視)
64
+
65
+ ---
66
+
67
+ ## 摩擦ポイント
68
+
69
+ ### F-1: `urlsafe_b64decode` が不正文字をサイレントに無視する(深刻度: 高)
70
+
71
+ **事象**: `decode_url_safe("!!!invalid!!!")` が `None` を返さず `b"\x8a{\xda\x96'"` を返した。
72
+
73
+ **原因**: `base64.urlsafe_b64decode()` は RFC 4648 の「ignore non-alphabet characters」モードで動作し、
74
+ `!` などの不正文字を黙って無視してデコードを続ける。
75
+ 一方、標準の `b64decode(s, validate=True)` は不正文字で `binascii.Error` を raise する。
76
+
77
+ **対応**: `urlsafe_b64decode` の前に文字セット正規表現で事前バリデーション:
78
+ ```python
79
+ _URL_SAFE_CHARS_RE = re.compile(r"^[A-Za-z0-9_\-=]*$")
80
+
81
+ def decode_url_safe(s: str) -> bytes | None:
82
+ stripped = s.rstrip("=")
83
+ if not stripped or not _URL_SAFE_CHARS_RE.match(stripped):
84
+ return None
85
+ ...
86
+ ```
87
+
88
+ **ライブラリ設計上の問題**: `urlsafe_b64decode` に `validate=True` パラメータが存在しない(`b64decode` にはある)。
89
+ URL セーフ版は自前バリデーションが必要という非対称な API 設計。
90
+
91
+ ---
92
+
93
+ ## 観察点
94
+
95
+ ### 観察1: 標準 base64 vs URL セーフ — `validate=True` の非対称性
96
+
97
+ ```python
98
+ # 標準版: validate パラメータがある
99
+ base64.b64decode("not!!!valid", validate=True) # → binascii.Error
100
+
101
+ # URL セーフ版: validate パラメータがない
102
+ base64.urlsafe_b64decode("not!!!valid") # → サイレントに無視してデコード
103
+ ```
104
+
105
+ JWT トークンや OAuth コードは URL セーフ base64 を使う。
106
+ `validate=True` に相当するガードを自前で実装しないと、
107
+ 不正トークンを誤って「有効」として処理する脆弱性になりうる。
108
+
109
+ ### 観察2: パディング補完の必要性
110
+
111
+ RFC 4648 §5 では URL セーフ base64 のパディング(`=`)は省略可能とされており、
112
+ JWT の `alg`・`typ` フィールドなど実際のトークンはパディングなしで渡ってくる。
113
+
114
+ ```python
115
+ # パディングなし JWT ヘッダーを補完してデコード
116
+ stripped = s.rstrip("=")
117
+ padding = 4 - len(stripped) % 4
118
+ if padding != 4:
119
+ s = stripped + "=" * padding
120
+ ```
121
+
122
+ `padding != 4` の条件で「すでに 4 の倍数の場合はパディングを追加しない」ことも重要。
123
+
124
+ ### 観察3: `partition(":")` で パスワード中のコロン対応
125
+
126
+ ```python
127
+ username, _, password = text.partition(":") # partition は最初の : で分割
128
+ ```
129
+
130
+ `split(":", 1)` でも同じだが `partition` の方が意図が明示的で、
131
+ `a:b:c:d` → `("a", ":", "b:c:d")` の分解が 1 行で書ける。
132
+
133
+ ### 観察4: データ URI の MIME タイプ検証
134
+
135
+ ```python
136
+ re.match(r"^[a-zA-Z0-9][a-zA-Z0-9!#$&\-^_]*\/[a-zA-Z0-9][a-zA-Z0-9!#$&\-^_.+]*$", mime_type)
137
+ ```
138
+
139
+ `image/png`・`application/octet-stream`・`text/plain; charset=utf-8` のような MIME タイプは正規表現で検証。
140
+ `javascript:`・`vbscript:` などの XSS ペイロードをデータ URI に埋め込む試みをブロックできる。
141
+
142
+ ---
143
+
144
+ ## nene2-python フレームワークとの統合
145
+
146
+ - `encode_basic_auth` / `parse_basic_auth` は `ApiKeyAuthMiddleware` の拡張として組み込み可能
147
+ - `make_data_uri` はファイルアップロード API のレスポンス形式として使える
148
+ - Pydantic `max_length` フィールドで全エンドポイントの DoS 対策済み
149
+ - `APIRouter` + `create_app()` パターン(FT177 摩擦 F-1 の対応)を最初から適用
150
+
151
+ ---
152
+
153
+ ## Developer Experience (DX) Review
154
+
155
+ ### ペルソナ1: 初心者(Python 歴1年・独学中・女性・バックエンド志望)
156
+
157
+ 画像アップロード API でクライアントから base64 エンコードされたデータを受け取る実装をしている。
158
+
159
+ **ドキュメント理解**: `b64encode` / `b64decode` のペアは分かりやすい。
160
+ URL セーフ版との違い(`+/` vs `-_`、パディング省略)はドキュメントに書いてないと気づきにくい。
161
+ **事故リスク**: 中。標準版と URL セーフ版を混在させると復号に失敗し、バイナリ化けとして現れる。
162
+ エラーより「壊れたデータ」として扱われるため気づきにくい。
163
+ **規約の使いやすさ**: `encode_standard(data)` が `str` を返し、`decode_standard(s)` が `bytes | None` を返すのは直感的。
164
+
165
+ ### ペルソナ2: ロースキル経験者(Python 歴3-4年・スクリプト系・男性・SES)
166
+
167
+ JWT パースのコードを既存プロジェクトからコピーしており、URL セーフ base64 を使っている。
168
+
169
+ **コピペ可能性**: `decode_url_safe` の自前バリデーションが必要な点はコメントがないと気づかない。
170
+ `urlsafe_b64decode` を直接使うと F-1 の罠にはまる。
171
+ **拡張時の罠**: パディング補完のコードを「なんか動いてるから削ってもいいかな」と消すと壊れる。
172
+ なぜ必要かのコメントが欲しい。
173
+ **セキュリティ的な事故リスク**: 高。JWT 検証で `urlsafe_b64decode` を `validate=True` なしで使うと、
174
+ 改ざんトークンが「デコード成功」として扱われる可能性がある。
175
+
176
+ ### ペルソナ3: フロントエンド寄り経験者(React/TS 歴4年・バックエンド転向中・ノンバイナリ)
177
+
178
+ TypeScript の `atob()` / `btoa()` に慣れており、Python で同じことをしようとしている。
179
+
180
+ **エラーレスポンスの質**: 400 Bad Request に具体的なメッセージ("Invalid base64 input" 等)が返るのは良い。
181
+ クライアント側でデバッグしやすい。
182
+ **Python 固有概念の学習コスト**: `bytes.hex()` / `bytes.fromhex()` の往復はTS では意識しない変換。
183
+ `atob()` は文字列を返すが `b64decode` は `bytes` を返す差異に戸惑う可能性。
184
+ **事故リスク**: 低。HTTP 境界での Pydantic バリデーションが充実。
185
+
186
+ ### ペルソナ4: バックエンド経験者(Django/FastAPI 歴5-6年・男性・リードエンジニア)
187
+
188
+ JWT ライブラリの内部実装を理解しており、raw base64 操作をすることもある。
189
+
190
+ **他フレームワークとの差異**: Django の `base64.urlsafe_b64decode` 直接利用パターンと同じ罠(F-1)が
191
+ nene2-python でも起きる。フレームワーク側での救済ではなく実装者が知識として持つ必要がある。
192
+ **nene2-python の薄さへの評価**: base64 は stdlib を薄くラップするだけが適切。
193
+ `decode_url_safe` のバリデーション付きラッパーは価値あるユーティリティ。
194
+ **本番投入可能性**: Basic Auth のパースは `parse_basic_auth` 一本で安全に使える設計が好評価。
195
+
196
+ ### ペルソナ5: シニアエンジニア(設計・コードレビュー担当・女性・10-12年)
197
+
198
+ **コードレビューチェックポイント**:
199
+ - [x] `urlsafe_b64decode` を自前バリデーションなしで使っていないか(F-1 の罠)
200
+ - [x] パスワードが base64 エンコードのみで「保護されている」と勘違いしていないか
201
+ (base64 は暗号化ではなくエンコード)
202
+ - [x] Basic Auth をデコードして得たパスワードをそのまま平文比較していないか
203
+ (`hmac.compare_digest` が必要)
204
+
205
+ **チームでの安全な共有パターン**: `decode_url_safe` に自前バリデーションを含めたラッパーを
206
+ ユーティリティとして共有すると、チーム全員が安全に使える。
207
+ **ツール追加の必要性**: `ruff` に base64 固有のルールはなし。コードレビューで担保。
208
+
209
+ ### ペルソナ6: 設計者・ポリシー照合(nene2-python 設計ポリシー目線)
210
+
211
+ **ポリシー達成度**: 高
212
+ **「初心者でも安全な API」達成度**: 中
213
+ — F-1(`urlsafe_b64decode` の無バリデーション問題)は初心者が直接 `base64.urlsafe_b64decode` を使うと再発する。
214
+ `decode_url_safe` ラッパーを使う運用を周知する必要がある。
215
+ **設計上の負債**: `validate=True` が URL セーフ版に存在しないのは Python stdlib の設計問題。
216
+ ユーザー向けに警告コメントを `decode_url_safe` に追記する価値がある。
217
+ **Follow-up Issue 候補**: なし(現状の実装で十分)
218
+
219
+ ---
220
+
221
+ ## Follow-up Issues
222
+
223
+ 今回の FT では実装上の重大な摩擦はなし(F-1 は実装内で解消済み)。
224
+
225
+ | 優先度 | タイトル | 種別 |
226
+ |---|---|---|
227
+ | 低 | `decode_url_safe` に「`urlsafe_b64decode` は validate=True がないため自前バリデーションが必要」コメントを追記 | docs |
228
+
229
+ ---
230
+
231
+ ## まとめ
232
+
233
+ FT178 では `base64` モジュールを中心に、Web API でよく使うエンコード操作を実装した。
234
+ 58 テストが全通過し、mypy/ruff も問題なし。
235
+
236
+ 最大の発見は F-1: `base64.urlsafe_b64decode` に `validate=True` がなくサイレントに不正入力を処理してしまう問題。
237
+ JWT・OAuth コード等を URL セーフ base64 で扱うコードが多い中、この挙動は高リスクな落とし穴。
238
+ 文字セット正規表現による事前バリデーションで対応済み。
239
+
240
+ APIRouter パターン(FT177 F-1 からの改善)を最初から適用し、テストが一発で通過した。
241
+
242
+ v1.8.49 としてリリース。
@@ -0,0 +1,229 @@
1
+ # Field Trial INDEX
2
+
3
+ フィールドトライアル一覧。テーマ・カテゴリ・診断種別から検索する用途を想定。
4
+
5
+ **凡例**:
6
+ - 🔒 セキュリティ診断実施(FT番号 % 3 = 0)
7
+ - 🔍 クラッカーペンテスト実施(FT172, 176, 180…)
8
+ - 列 "Follow-up" に関連 GitHub Issue 番号を記載
9
+
10
+ ---
11
+
12
+ ## カテゴリ1: 初期統合検証 (FT1〜FT18)
13
+
14
+ | FT# | テーマ | 診断 | Follow-up |
15
+ |-----|--------|------|-----------|
16
+ | [FT1](2026-05-field-trial-1.md) | lunchlog — git+ インストール・新規プロジェクト構築 | | |
17
+ | [FT2](2026-05-field-trial-2.md) | bookshelf — SQLite 永続化リポジトリ DX | | |
18
+ | [FT3](2026-05-field-trial-3.md) | tasklist — Bearer Token 認証 + MCP サーバー | 🔒 | |
19
+ | [FT4](2026-05-field-trial-4.md) | snippets — SQLite 共有 MCP・ApiKey 認証・CORS | | |
20
+ | [FT5](2026-05-field-trial-5.md) | wallet — transactional() DX | | |
21
+ | [FT6](2026-05-field-trial-6.md) | weather — AsyncUseCaseProtocol DX | 🔒 | |
22
+ | [FT7](2026-05-field-trial-7.md) | bookmark — PyPI publish フロー | | |
23
+ | [FT8](2026-05-field-trial-8.md) | blog — 親子リソース (Nested REST) + datetime | | |
24
+ | [FT9](2026-05-field-trial-9.md) | recipe — HttpxMcpClient + streamable-http | 🔒 | |
25
+ | [FT10](2026-05-field-trial-10.md) | inventory — MySQL アダプター | | |
26
+ | [FT11](2026-05-field-trial-11.md) | journal — BearerTokenMiddleware + HttpxMcpClient | | |
27
+ | [FT12](2026-05-field-trial-12.md) | ThrottleMiddleware + RequestSizeLimitMiddleware | 🔒 | |
28
+ | [FT13](2026-05-field-trial-13.md) | ValidationException 実運用 | | |
29
+ | [FT14](2026-05-field-trial-14.md) | AsyncUseCaseProtocol 実運用 | | |
30
+ | [FT15](2026-05-field-trial-15.md) | SecurityHeadersMiddleware 実運用 | 🔒 | |
31
+ | [FT16](2026-05-field-trial-16.md) | transactional(callback) パターン | | |
32
+ | [FT17](2026-05-field-trial-17.md) | 複数ドメイン連携 | | |
33
+ | [FT18](2026-05-field-trial-18.md) | RequestLoggingMiddleware 実運用 | 🔒 | |
34
+
35
+ ---
36
+
37
+ ## カテゴリ2: フレームワーク実運用検証 (FT19〜FT80)
38
+
39
+ | FT# | テーマ | 診断 | Follow-up |
40
+ |-----|--------|------|-----------|
41
+ | [FT19](2026-05-field-trial-19.md) | problem_details_response() RFC 9457 | | |
42
+ | [FT20](2026-05-field-trial-20.md) | ThrottleMiddleware 実運用 | | |
43
+ | [FT21](2026-05-field-trial-21.md) | DomainExceptionHandler | 🔒 | |
44
+ | [FT22](2026-05-field-trial-22.md) | HealthCheckProtocol | | |
45
+ | [FT23](2026-05-field-trial-23.md) | RequestSizeLimitMiddleware | | |
46
+ | [FT24](2026-05-field-trial-24.md) | AppSettings | 🔒 | |
47
+ | [FT25](2026-05-field-trial-25.md) | RequestIdMiddleware | | |
48
+ | [FT26](2026-05-field-trial-26.md) | nene2.log structlog 統合 | | |
49
+ | [FT27](2026-05-field-trial-27.md) | ThrottleMiddleware 長時間運用 | 🔒 | |
50
+ | [FT28](2026-05-field-trial-28.md) | ThrottleMiddleware パスごとレート制限 | | |
51
+ | [FT29](2026-05-field-trial-29.md) | AsyncUseCaseProtocol 実運用 | | |
52
+ | [FT30](2026-05-field-trial-30.md) | RequestLoggingMiddleware + structlog バインディング | 🔒 | |
53
+ | [FT31](2026-05-field-trial-31.md) | DatabaseHealthCheck + ヘルスエンドポイント | | |
54
+ | [FT32](2026-05-field-trial-32.md) | SecurityHeadersMiddleware CSP カスタマイズ | | |
55
+ | [FT33](2026-05-field-trial-33.md) | ValidationException カスタムエラーコード | 🔒 | |
56
+ | [FT34](2026-05-field-trial-34.md) | DatabaseIntegrityException + SimpleDomainHandler | | |
57
+ | [FT35](2026-05-field-trial-35.md) | 混合認証(Bearer Token OR API Key) | | |
58
+ | [FT36](2026-05-field-trial-36.md) | CompositeHealthCheck | 🔒 | |
59
+ | [FT37](2026-05-field-trial-37.md) | PaginationResponse + PaginationQueryParser | | |
60
+ | [FT38](2026-05-field-trial-38.md) | SqlAlchemyTransactionManager.transactional() | | |
61
+ | [FT39](2026-05-field-trial-39.md) | RequestSizeLimitMiddleware | 🔒 | |
62
+ | [FT40](2026-05-field-trial-40.md) | 多ドメイン連携(Article + Tag) | | |
63
+ | [FT41](2026-05-field-trial-41.md) | structlog テスト統合 — configure_for_testing() + caplog | | |
64
+ | [FT42](2026-05-field-trial-42.md) | get_request_id() Depends + configure_problem_details() | 🔒 | |
65
+ | [FT43](2026-05-field-trial-43.md) | ThrottleMiddleware path_limits | | |
66
+ | [FT44](2026-05-field-trial-44.md) | PaginationQueryParser + PaginationResponse | | |
67
+ | [FT45](2026-05-field-trial-45.md) | SecurityHeadersMiddleware 詳細カスタマイズ | 🔒 | |
68
+ | [FT46](2026-05-field-trial-46.md) | DatabaseIntegrityException | | |
69
+ | [FT47](2026-05-field-trial-47.md) | SqlAlchemyTransactionManager.transactional() | | |
70
+ | [FT48](2026-05-field-trial-48.md) | CompositeHealthCheck + AsyncCompositeHealthCheck | 🔒 | |
71
+ | [FT49](2026-05-field-trial-49.md) | AppSettings | | |
72
+ | [FT50](2026-05-field-trial-50.md) | ValidationException + ValidationCode(StrEnum) | | |
73
+ | [FT51](2026-05-field-trial-51.md) | SimpleDomainHandler | 🔒 | |
74
+ | [FT52](2026-05-field-trial-52.md) | ミドルウェアスタック組み合わせ | | |
75
+ | [FT53](2026-05-field-trial-53.md) | ApiKeyAuthMiddleware | | |
76
+ | [FT54](2026-05-field-trial-54.md) | RequestSizeLimitMiddleware + path_limits | 🔒 | |
77
+ | [FT55](2026-05-field-trial-55.md) | parse_db_datetime | | |
78
+ | [FT56](2026-05-field-trial-56.md) | CompositeHealthCheck + http_status_code | | |
79
+ | [FT57](2026-05-field-trial-57.md) | AsyncCompositeHealthCheck | 🔒 | |
80
+ | [FT58](2026-05-field-trial-58.md) | ThrottleMiddleware | | |
81
+ | [FT59](2026-05-field-trial-59.md) | SecurityHeadersMiddleware | | |
82
+ | [FT60](2026-05-field-trial-60.md) | RequestIdMiddleware | 🔒 | |
83
+ | [FT61](2026-05-field-trial-61.md) | AsyncUseCaseProtocol | | |
84
+ | [FT62](2026-05-field-trial-62.md) | RequestLoggingMiddleware | | |
85
+ | [FT63](2026-05-field-trial-63.md) | configure_problem_details / PROBLEM_DETAILS_BASE_URL | 🔒 | |
86
+ | [FT64](2026-05-field-trial-64.md) | ValidationException 複数エラー | | |
87
+ | [FT65](2026-05-field-trial-65.md) | DatabaseHealthCheck | | |
88
+ | [FT66](2026-05-field-trial-66.md) | AppSettings | 🔒 | |
89
+ | [FT67](2026-05-field-trial-67.md) | SqlAlchemyTransactionManager | | |
90
+ | [FT68](2026-05-field-trial-68.md) | SimpleDomainHandler + extra_factory | | |
91
+ | [FT69](2026-05-field-trial-69.md) | PaginationQueryParser + PaginationResponse | 🔒 | |
92
+ | [FT70](2026-05-field-trial-70.md) | 複数ドメイン連携 | | |
93
+ | [FT71](2026-05-field-trial-71.md) | 完全レイヤードアーキテクチャ | | |
94
+ | [FT72](2026-05-field-trial-72.md) | DatabaseIntegrityException + write() 戻り値パターン | 🔒 | |
95
+ | [FT73](2026-05-field-trial-73.md) | PaginationQueryParser.parse() 静的メソッド | | |
96
+ | [FT74](2026-05-field-trial-74.md) | カスタム HealthCheckProtocol 実装 | | |
97
+ | [FT75](2026-05-field-trial-75.md) | ミドルウェアスタック順序依存性 | 🔒 | |
98
+ | [FT76](2026-05-field-trial-76.md) | async ハンドラー + sync SQLAlchemy イベントループブロッキング | | |
99
+ | [FT77](2026-05-field-trial-77.md) | BearerToken + ApiKey 混在認証 | | |
100
+ | [FT78](2026-05-field-trial-78.md) | ThrottleMiddleware 境界動作 | 🔒 | |
101
+ | [FT79](2026-05-field-trial-79.md) | RequestLoggingMiddleware と構造化ログ | | |
102
+ | [FT80](2026-05-field-trial-80.md) | MCP E2E — LocalMcpServer + HttpxMcpClient | | |
103
+
104
+ ---
105
+
106
+ ## カテゴリ3: 応用パターン検証 (FT81〜FT120)
107
+
108
+ | FT# | テーマ | 診断 | Follow-up |
109
+ |-----|--------|------|-----------|
110
+ | [FT81](2026-05-field-trial-81.md) | CORS — setup_middlewares() + CORSMiddleware | 🔒 | |
111
+ | [FT82](2026-05-field-trial-82.md) | Background Tasks — FastAPI BackgroundTasks | | |
112
+ | [FT83](2026-05-field-trial-83.md) | Depends() DI — nene2 アーキテクチャ統合 | | |
113
+ | [FT84](2026-05-field-trial-84.md) | 認証 Depends ユーティリティ — CurrentUser / require_auth | 🔒 | |
114
+ | [FT85](2026-05-field-trial-85.md) | OpenAPI スキーマ品質 — JSONResponse と response_model | | |
115
+ | [FT86](2026-05-field-trial-86.md) | Lifespan イベント — startup/shutdown | | |
116
+ | [FT87](2026-05-field-trial-87.md) | カスタムレスポンスヘッダー — X-Total-Count / X-RateLimit | 🔒 | |
117
+ | [FT88](2026-05-field-trial-88.md) | ドメインイベント — 同期イベントバスパターン | | |
118
+ | [FT89](2026-05-field-trial-89.md) | カスタムバリデーション — Pydantic + ValidationException 統合 | | |
119
+ | [FT90](2026-05-field-trial-90.md) | ファイルアップロード — multipart/form-data | 🔒 | |
120
+ | [FT91](2026-05-field-trial-91.md) | ストリーミングレスポンス — StreamingResponse + SSE | | |
121
+ | [FT92](2026-05-field-trial-92.md) | APIRouter パターン | | |
122
+ | [FT93](2026-05-field-trial-93.md) | Dependency Override パターン | 🔒 | |
123
+ | [FT94](2026-05-field-trial-94.md) | ミドルウェア順序とエラーレスポンスのヘッダー | | |
124
+ | [FT95](2026-05-field-trial-95.md) | Pydantic model_validator / field_validator + nene2 統合 | | |
125
+ | [FT96](2026-05-field-trial-96.md) | カスタム例外ハンドラーと ErrorHandlerMiddleware 共存 | 🔒 | |
126
+ | [FT97](2026-05-field-trial-97.md) | HTTP キャッシュヘッダー (ETag / Cache-Control) | | |
127
+ | [FT98](2026-05-field-trial-98.md) | PATCH / Partial Update パターン | | |
128
+ | [FT99](2026-05-field-trial-99.md) | Webhook HMAC-SHA256 署名検証 | 🔒 | |
129
+ | [FT100](2026-05-field-trial-100.md) | In-memory TTL レスポンスキャッシュ | | |
130
+ | [FT101](2026-05-field-trial-101.md) | Query Parameter Filter/Sort パターン | | |
131
+ | [FT102](2026-05-field-trial-102.md) | response_model と PaginationResponse の型整合性 | 🔒 | |
132
+ | [FT103](2026-05-field-trial-103.md) | カスタムミドルウェアで認証情報をリクエストスコープに格納 | | |
133
+ | [FT104](2026-05-field-trial-104.md) | AsyncIterator を返す UseCase + StreamingResponse | | |
134
+ | [FT105](2026-05-field-trial-105.md) | マルチテナント DB 接続 | 🔒 | |
135
+ | [FT106](2026-05-field-trial-106.md) | Idempotency Key パターン | | |
136
+ | [FT107](2026-05-field-trial-107.md) | Bulk Operations(一括作成・削除) | | |
137
+ | [FT108](2026-05-field-trial-108.md) | Pydantic computed_field と property パターン | 🔒 | |
138
+ | [FT109](2026-05-field-trial-109.md) | API バージョニング(v1/v2 ルーティング) | | |
139
+ | [FT110](2026-05-field-trial-110.md) | ソフトデリート(論理削除)パターン | | |
140
+ | [FT111](2026-05-field-trial-111.md) | カーソルベースページネーション | 🔒 | |
141
+ | [FT112](2026-05-field-trial-112.md) | バルクアップデート(PATCH /items/bulk) | | |
142
+ | [FT113](2026-05-field-trial-113.md) | Pydantic 識別共用体(Discriminated Union) | | |
143
+ | [FT114](2026-05-field-trial-114.md) | プラグインレジストリパターン | 🔒 | |
144
+ | [FT115](2026-05-field-trial-115.md) | structlog 構造化ログ + リクエストコンテキスト伝播 | | |
145
+ | [FT116](2026-05-field-trial-116.md) | @contextmanager リソース管理 + FastAPI lifespan | | |
146
+ | [FT117](2026-05-field-trial-117.md) | TypedDict + @overload による型安全な辞書操作 | 🔒 | |
147
+ | [FT118](2026-05-field-trial-118.md) | Python match 文パターンマッチング | | |
148
+ | [FT119](2026-05-field-trial-119.md) | functools.lru_cache / cache による関数レベルキャッシュ | | |
149
+ | [FT120](2026-05-field-trial-120.md) | dataclasses.field 高度な使い方 | 🔒 | |
150
+
151
+ ---
152
+
153
+ ## カテゴリ4: Python 標準ライブラリ検証 (FT121〜FT177)
154
+
155
+ | FT# | テーマ | 診断 | Follow-up |
156
+ |-----|--------|------|-----------|
157
+ | [FT121](2026-05-field-trial-121.md) | asyncio.gather + asyncio.TaskGroup 並列処理 | 🔒 | |
158
+ | [FT122](2026-05-field-trial-122.md) | 型安全なインプロセス イベントバス | | |
159
+ | [FT123](2026-05-field-trial-123.md) | pydantic.SecretStr + 環境変数セキュアな設定管理 | | |
160
+ | [FT124](2026-05-field-trial-124.md) | pathlib.Path + セキュアなファイル操作 | 🔒 | |
161
+ | [FT125](2026-05-field-trial-125.md) | Python 3.12 ジェネリッククラス + Pydantic Generic | | |
162
+ | [FT126](2026-05-field-trial-126.md) | StrEnum / IntEnum の高度な活用 | | |
163
+ | [FT127](2026-05-field-trial-127.md) | collections モジュールの高度な活用 | 🔒 | |
164
+ | [FT128](2026-05-field-trial-128.md) | itertools モジュール | | |
165
+ | [FT129](2026-05-field-trial-129.md) | functools モジュール | | |
166
+ | [FT130](2026-05-field-trial-130.md) | operator + heapq + bisect | 🔒 | |
167
+ | [FT131](2026-05-field-trial-131.md) | デスクリプタープロトコル | | |
168
+ | [FT132](2026-05-field-trial-132.md) | __init_subclass__ + クラスデコレーター | | |
169
+ | [FT133](2026-05-field-trial-133.md) | contextlib の高度な活用 | 🔒 | |
170
+ | [FT134](2026-05-field-trial-134.md) | typing モジュール | | |
171
+ | [FT135](2026-05-field-trial-135.md) | abc モジュール + 抽象基底クラス | | |
172
+ | [FT136](2026-05-field-trial-136.md) | re モジュール | 🔒 | |
173
+ | [FT137](2026-05-field-trial-137.md) | datetime + zoneinfo | | |
174
+ | [FT138](2026-05-field-trial-138.md) | decimal + fractions + statistics | | |
175
+ | [FT139](2026-05-field-trial-139.md) | concurrent.futures + threading | 🔒 | |
176
+ | [FT140](2026-05-field-trial-140.md) | io モジュール + バイナリ処理 | | |
177
+ | [FT141](2026-05-field-trial-141.md) | asyncio 高度機能 | | |
178
+ | [FT142](2026-05-field-trial-142.md) | uuid モジュール | 🔒 | |
179
+ | [FT143](2026-05-field-trial-143.md) | inspect モジュール | | |
180
+ | [FT144](2026-05-field-trial-144.md) | logging 高度機能 | | |
181
+ | [FT145](2026-05-field-trial-145.md) | weakref モジュール | 🔒 | |
182
+ | [FT146](2026-05-field-trial-146.md) | copy モジュール | | |
183
+ | [FT147](2026-05-field-trial-147.md) | urllib.parse モジュール | | |
184
+ | [FT148](2026-05-field-trial-148.md) | json 高度機能 | 🔒 | |
185
+ | [FT149](2026-05-field-trial-149.md) | csv モジュール | | |
186
+ | [FT150](2026-05-field-trial-150.md) | configparser モジュール | | |
187
+ | [FT151](2026-05-field-trial-151.md) | argparse モジュール | 🔒 | |
188
+ | [FT152](2026-05-field-trial-152.md) | hashlib モジュール(初回) | | |
189
+ | [FT153](2026-05-field-trial-153.md) | struct モジュール | | |
190
+ | [FT154](2026-05-field-trial-154.md) | array モジュールと memoryview | 🔒 | |
191
+ | [FT155](2026-05-field-trial-155.md) | queue モジュール | | |
192
+ | [FT156](2026-05-field-trial-156.md) | threading モジュール | | |
193
+ | [FT157](2026-05-field-trial-157.md) | multiprocessing モジュール | 🔒 | |
194
+ | [FT158](2026-05-field-trial-158.md) | tempfile モジュール | | |
195
+ | [FT159](2026-05-field-trial-159.md) | fnmatch と glob モジュール | | |
196
+ | [FT160](2026-05-field-trial-160.md) | difflib モジュール | 🔒 | |
197
+ | [FT161](2026-05-field-trial-161.md) | shutil モジュール | | |
198
+ | [FT162](2026-05-field-trial-162.md) | zipfile モジュール | | |
199
+ | [FT163](2026-05-field-trial-163.md) | sqlite3 モジュール | 🔒 | |
200
+ | [FT164](2026-05-field-trial-164.md) | contextlib モジュール(再検証) | | |
201
+ | [FT165](2026-05-field-trial-165.md) | secrets モジュール | | |
202
+ | [FT166](2026-05-field-trial-166.md) | functools モジュール(再検証) | 🔒 | |
203
+ | [FT167](2026-05-field-trial-167.md) | enum モジュール | | |
204
+ | [FT168](2026-05-field-trial-168.md) | re モジュール(再検証) | | |
205
+ | [FT169](2026-05-field-trial-169.md) | typing モジュール(再検証) | 🔒 | |
206
+ | [FT170](2026-05-field-trial-170.md) | collections モジュール(再検証) | | |
207
+ | [FT171](2026-05-field-trial-171.md) | asyncio モジュール(再検証) | | |
208
+ | [FT172](2026-05-field-trial-172.md) | dataclasses モジュール | 🔒🔍 | |
209
+ | [FT173](2026-05-field-trial-173.md) | pathlib モジュール(再検証) | | |
210
+ | [FT174](2026-05-field-trial-174.md) | itertools モジュール(再検証) | 🔒 | |
211
+ | [FT175](2026-05-field-trial-175.md) | logging モジュール — SensitiveFilter / RequestIdAdapter | | |
212
+ | [FT176](2026-05-field-trial-176.md) | decimal モジュール — 金融計算・精度制御 | 🔍 | [#499](https://github.com/hideyukiMORI/nene2-python/issues/499) [#500](https://github.com/hideyukiMORI/nene2-python/issues/500) |
213
+ | [FT177](2026-05-field-trial-177.md) | hashlib モジュール — PBKDF2 / scrypt / Blake2 | 🔒 | [#501](https://github.com/hideyukiMORI/nene2-python/issues/501) |
214
+
215
+ ---
216
+
217
+ ## セキュリティ診断実施済み一覧(🔒)
218
+
219
+ FT3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99, 102, 105, 108, 111, 114, 117, 120, 121, 124, 127, 130, 133, 136, 139, 142, 145, 148, 151, 154, 157, 160, 163, 166, 169, 172, 174, 177
220
+
221
+ 合計: **60件**(177 FT 中 約 34%)
222
+
223
+ ## クラッカーペンテスト実施済み一覧(🔍)
224
+
225
+ FT172, FT176
226
+
227
+ ---
228
+
229
+ *最終更新: 2026-05-21 (FT177 / v1.8.48)*
@@ -0,0 +1,63 @@
1
+ # TODO — current
2
+
3
+ 最終更新: 2026-05-21
4
+ 現状: **v1.8.48 安定版 / フィールドトライアルループ継続中(FT177 完了)**
5
+
6
+ ---
7
+
8
+ ## 状態サマリー
9
+
10
+ v1.8.48 完了済み。FT177(hashlib / PBKDF2・scrypt・Blake2)を含む FT177 件を実施済み。
11
+ フィールドトライアルループは FT178 以降も継続中。
12
+
13
+ ---
14
+
15
+ ## オープン PR
16
+
17
+ なし(現在 main ブランチはクリーン)
18
+
19
+ ---
20
+
21
+ ## オープン Issue(優先度順)
22
+
23
+ | Issue | 内容 | 優先度 |
24
+ |---|---|---|
25
+ | [#501](https://github.com/hideyukiMORI/nene2-python/issues/501) | [FT177] FastAPI アプリファクトリで APIRouter パターンを標準化 | 中 |
26
+ | [#500](https://github.com/hideyukiMORI/nene2-python/issues/500) | [FT176] parse_decimal_safe() の Unicode 全角数字受け入れ挙動を文書化 | 低 |
27
+ | [#499](https://github.com/hideyukiMORI/nene2-python/issues/499) | [FT176] calculate_tax/discount にビジネスロジックバリデーション追加 | 中 |
28
+
29
+ ---
30
+
31
+ ## 直近の完了マイルストーン
32
+
33
+ | バージョン | 主な内容 |
34
+ |---|---|
35
+ | v1.8.48 | FT177: hashlib — PBKDF2 / scrypt / Blake2 キー付きハッシュ |
36
+ | v1.8.47 | FT176: decimal — 金融計算・精度制御(クラッカーペンテスト実施) |
37
+ | v1.8.46 | FT175: logging — SensitiveFilter / RequestIdAdapter |
38
+ | v1.8.45 | FT174: itertools — 安全な組み合わせ計算(セキュリティ診断実施) |
39
+ | v1.8.44 | FT173: pathlib — セキュアなファイル操作 |
40
+ | v1.8.43 | FT172: dataclasses — フリーズ・スロット・バリデーション(診断+ペンテスト) |
41
+
42
+ ---
43
+
44
+ ## フィールドトライアル進捗
45
+
46
+ **実施済み**: FT1〜FT177(全 177 件)
47
+
48
+ 索引: [`docs/field-trials/INDEX.md`](../field-trials/INDEX.md)
49
+
50
+ **次のアクション**:
51
+ - FT178 以降を継続(FT180 は 180 % 4 = 0 → クラッカーペンテスト対象)
52
+ - FT180 は 180 % 3 = 0 → セキュリティ診断も実施
53
+
54
+ ---
55
+
56
+ ## 改善検討事項
57
+
58
+ | 課題 | 優先度 | 備考 |
59
+ |---|---|---|
60
+ | PostgreSQL / MySQL 実 DB 統合テスト | 中〜高 | CI に Docker service ジョブを追加検討 |
61
+ | PyJWT 推移的 CVE(PYSEC-2025-183) | 低 | mcp 側の修正を待ち。文書化済み |
62
+ | APIRouter パターンを FT テンプレートに反映 | 中 | Issue #501 |
63
+ | FT サマリ索引 | 完了 | `docs/field-trials/INDEX.md` 作成済み |
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nene2-python"
3
- version = "1.8.48"
3
+ version = "1.8.49"
4
4
  description = "NENE2 Python — minimal API framework following NENE2's design philosophy"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}
@@ -60,6 +60,7 @@ dev = [
60
60
  "mypy>=1.13",
61
61
  "ruff>=0.9",
62
62
  "pip-audit>=2.7",
63
+ "psycopg2-binary>=2.9.12",
63
64
  ]
64
65
 
65
66
  # ---------------------------------------------------------------------------
@@ -1,52 +0,0 @@
1
- # TODO — current
2
-
3
- 最終更新: 2026-05-20
4
- 現状: **v1.8.33 安定版 / フィールドトライアルループ継続中**
5
-
6
- ---
7
-
8
- ## 状態サマリー
9
-
10
- v1.8.33 完了済み。フィールドトライアル FT108/FT109 を含む docs 改善マージ済み。
11
- 2026-05-20 時点でオープン PR は PR #428(ResourceWarning 修正)のみ。
12
-
13
- ---
14
-
15
- ## オープン PR
16
-
17
- | PR | Issue | 内容 |
18
- |---|---|---|
19
- | [#428](https://github.com/hideyukiMORI/nene2-python/pull/428) | #427 | テストの ResourceWarning: unclosed database を解消する |
20
-
21
- ---
22
-
23
- ## 直近の完了マイルストーン
24
-
25
- | バージョン | 主な追加機能 |
26
- |---|---|
27
- | v1.8.33 | `nene2.cache.TtlCache[V]` |
28
- | v1.8.32 | `nene2.security.verify_hmac_signature()` |
29
- | v1.8.31 | `nene2.http.generate_etag()` |
30
- | v1.8.30 | `problem_details_response()` headers パラメーター |
31
- | v1.8.29 | `make_require_auth()` |
32
- | v1.8.28 | `PaginationDep`, `PaginationResponse.model_dump()` |
33
-
34
- ---
35
-
36
- ## フィールドトライアル進捗
37
-
38
- **実施済み**: FT1〜FT109(FT108, FT109 含む docs-only)
39
-
40
- **次のアクション候補**(優先度順):
41
- 1. ResourceWarning 修正 PR #428 マージ後、バグ修正リリース(v1.8.34)
42
- 2. FT110+ — 未検証パターンの探索継続
43
- 3. DB 実統合テスト(PostgreSQL/MySQL 実環境)の追加検討
44
- 4. PyPI 公開体験の最終確認
45
-
46
- ---
47
-
48
- ## 改善検討事項
49
-
50
- - 警告ゼロ化: PR #428 でほぼ解消(StaticPool の 1 件は filterwarnings で抑制)
51
- - DB 実統合テスト: SQLite インメモリテストはあるが PostgreSQL/MySQL 実環境テストは未
52
- - PyPI 公開体験の仕上げ: パッケージメタデータ整備済み、公開フロー最終確認が残
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes