nene2-python 1.8.62__tar.gz → 1.8.64__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 (416) hide show
  1. {nene2_python-1.8.62 → nene2_python-1.8.64}/PKG-INFO +1 -1
  2. nene2_python-1.8.64/docs/field-trials/2026-05-field-trial-191.md +214 -0
  3. nene2_python-1.8.64/docs/field-trials/2026-05-field-trial-192.md +485 -0
  4. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/INDEX.md +4 -2
  5. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/todo/current.md +7 -5
  6. {nene2_python-1.8.62 → nene2_python-1.8.64}/pyproject.toml +1 -1
  7. {nene2_python-1.8.62 → nene2_python-1.8.64}/.env.example +0 -0
  8. {nene2_python-1.8.62 → nene2_python-1.8.64}/.github/workflows/ci.yml +0 -0
  9. {nene2_python-1.8.62 → nene2_python-1.8.64}/.github/workflows/docs.yml +0 -0
  10. {nene2_python-1.8.62 → nene2_python-1.8.64}/.github/workflows/publish.yml +0 -0
  11. {nene2_python-1.8.62 → nene2_python-1.8.64}/.gitignore +0 -0
  12. {nene2_python-1.8.62 → nene2_python-1.8.64}/.vitepress/config.mts +0 -0
  13. {nene2_python-1.8.62 → nene2_python-1.8.64}/.vitepress/theme/custom.css +0 -0
  14. {nene2_python-1.8.62 → nene2_python-1.8.64}/.vitepress/theme/index.ts +0 -0
  15. {nene2_python-1.8.62 → nene2_python-1.8.64}/AGENTS.md +0 -0
  16. {nene2_python-1.8.62 → nene2_python-1.8.64}/CHANGELOG.md +0 -0
  17. {nene2_python-1.8.62 → nene2_python-1.8.64}/CLAUDE.md +0 -0
  18. {nene2_python-1.8.62 → nene2_python-1.8.64}/Dockerfile +0 -0
  19. {nene2_python-1.8.62 → nene2_python-1.8.64}/LICENSE +0 -0
  20. {nene2_python-1.8.62 → nene2_python-1.8.64}/README.md +0 -0
  21. {nene2_python-1.8.62 → nene2_python-1.8.64}/alembic/README +0 -0
  22. {nene2_python-1.8.62 → nene2_python-1.8.64}/alembic/env.py +0 -0
  23. {nene2_python-1.8.62 → nene2_python-1.8.64}/alembic/script.py.mako +0 -0
  24. {nene2_python-1.8.62 → nene2_python-1.8.64}/alembic/versions/001_create_notes_and_tags_tables.py +0 -0
  25. {nene2_python-1.8.62 → nene2_python-1.8.64}/alembic.ini +0 -0
  26. {nene2_python-1.8.62 → nene2_python-1.8.64}/compose.yaml +0 -0
  27. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/adr/0001-toolchain.md +0 -0
  28. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/adr/0002-clean-architecture.md +0 -0
  29. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/adr/0003-security-first.md +0 -0
  30. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/adr/0004-ai-first-design.md +0 -0
  31. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/adr/0005-logging.md +0 -0
  32. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/adr/0006-rate-limiting.md +0 -0
  33. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/adr/0009-mcp-design.md +0 -0
  34. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/adr/0010-async-use-case.md +0 -0
  35. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/adr/0011-mcp-as-core-dependency.md +0 -0
  36. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/de/index.md +0 -0
  37. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/de/tutorials/getting-started.md +0 -0
  38. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/explanation/architecture.md +0 -0
  39. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/explanation/design-philosophy.md +0 -0
  40. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-1.md +0 -0
  41. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-10.md +0 -0
  42. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-100.md +0 -0
  43. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-101.md +0 -0
  44. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-102.md +0 -0
  45. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-103.md +0 -0
  46. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-104.md +0 -0
  47. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-105.md +0 -0
  48. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-106.md +0 -0
  49. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-107.md +0 -0
  50. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-108.md +0 -0
  51. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-109.md +0 -0
  52. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-11.md +0 -0
  53. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-110.md +0 -0
  54. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-111.md +0 -0
  55. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-112.md +0 -0
  56. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-113.md +0 -0
  57. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-114.md +0 -0
  58. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-115.md +0 -0
  59. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-116.md +0 -0
  60. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-117.md +0 -0
  61. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-118.md +0 -0
  62. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-119.md +0 -0
  63. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-12.md +0 -0
  64. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-120.md +0 -0
  65. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-121.md +0 -0
  66. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-122.md +0 -0
  67. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-123.md +0 -0
  68. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-124.md +0 -0
  69. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-125.md +0 -0
  70. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-126.md +0 -0
  71. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-127.md +0 -0
  72. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-128.md +0 -0
  73. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-129.md +0 -0
  74. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-13.md +0 -0
  75. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-130.md +0 -0
  76. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-131.md +0 -0
  77. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-132.md +0 -0
  78. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-133.md +0 -0
  79. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-134.md +0 -0
  80. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-135.md +0 -0
  81. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-136.md +0 -0
  82. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-137.md +0 -0
  83. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-138.md +0 -0
  84. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-139.md +0 -0
  85. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-14.md +0 -0
  86. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-140.md +0 -0
  87. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-141.md +0 -0
  88. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-142.md +0 -0
  89. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-143.md +0 -0
  90. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-144.md +0 -0
  91. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-145.md +0 -0
  92. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-146.md +0 -0
  93. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-147.md +0 -0
  94. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-148.md +0 -0
  95. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-149.md +0 -0
  96. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-15.md +0 -0
  97. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-150.md +0 -0
  98. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-151.md +0 -0
  99. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-152.md +0 -0
  100. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-153.md +0 -0
  101. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-154.md +0 -0
  102. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-155.md +0 -0
  103. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-156.md +0 -0
  104. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-157.md +0 -0
  105. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-158.md +0 -0
  106. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-159.md +0 -0
  107. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-16.md +0 -0
  108. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-160.md +0 -0
  109. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-161.md +0 -0
  110. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-162.md +0 -0
  111. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-163.md +0 -0
  112. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-164.md +0 -0
  113. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-165.md +0 -0
  114. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-166.md +0 -0
  115. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-167.md +0 -0
  116. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-168.md +0 -0
  117. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-169.md +0 -0
  118. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-17.md +0 -0
  119. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-170.md +0 -0
  120. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-171.md +0 -0
  121. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-172.md +0 -0
  122. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-173.md +0 -0
  123. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-174.md +0 -0
  124. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-175.md +0 -0
  125. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-176.md +0 -0
  126. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-177.md +0 -0
  127. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-178.md +0 -0
  128. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-179.md +0 -0
  129. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-18.md +0 -0
  130. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-180.md +0 -0
  131. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-181.md +0 -0
  132. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-182.md +0 -0
  133. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-183.md +0 -0
  134. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-184.md +0 -0
  135. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-185.md +0 -0
  136. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-186.md +0 -0
  137. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-187.md +0 -0
  138. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-188.md +0 -0
  139. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-189.md +0 -0
  140. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-19.md +0 -0
  141. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-190.md +0 -0
  142. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-2.md +0 -0
  143. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-20.md +0 -0
  144. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-21.md +0 -0
  145. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-22.md +0 -0
  146. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-23.md +0 -0
  147. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-24.md +0 -0
  148. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-25.md +0 -0
  149. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-26.md +0 -0
  150. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-27.md +0 -0
  151. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-28.md +0 -0
  152. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-29.md +0 -0
  153. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-3.md +0 -0
  154. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-30.md +0 -0
  155. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-31.md +0 -0
  156. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-32.md +0 -0
  157. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-33.md +0 -0
  158. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-34.md +0 -0
  159. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-35.md +0 -0
  160. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-36.md +0 -0
  161. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-37.md +0 -0
  162. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-38.md +0 -0
  163. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-39.md +0 -0
  164. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-4.md +0 -0
  165. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-40.md +0 -0
  166. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-41.md +0 -0
  167. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-42.md +0 -0
  168. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-43.md +0 -0
  169. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-44.md +0 -0
  170. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-45.md +0 -0
  171. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-46.md +0 -0
  172. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-47.md +0 -0
  173. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-48.md +0 -0
  174. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-49.md +0 -0
  175. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-5.md +0 -0
  176. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-50.md +0 -0
  177. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-51.md +0 -0
  178. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-52.md +0 -0
  179. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-53.md +0 -0
  180. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-54.md +0 -0
  181. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-55.md +0 -0
  182. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-56.md +0 -0
  183. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-57.md +0 -0
  184. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-58.md +0 -0
  185. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-59.md +0 -0
  186. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-6.md +0 -0
  187. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-60.md +0 -0
  188. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-61.md +0 -0
  189. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-62.md +0 -0
  190. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-63.md +0 -0
  191. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-64.md +0 -0
  192. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-65.md +0 -0
  193. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-66.md +0 -0
  194. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-67.md +0 -0
  195. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-68.md +0 -0
  196. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-69.md +0 -0
  197. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-7.md +0 -0
  198. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-70.md +0 -0
  199. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-71.md +0 -0
  200. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-72.md +0 -0
  201. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-73.md +0 -0
  202. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-74.md +0 -0
  203. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-75.md +0 -0
  204. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-76.md +0 -0
  205. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-77.md +0 -0
  206. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-78.md +0 -0
  207. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-79.md +0 -0
  208. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-8.md +0 -0
  209. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-80.md +0 -0
  210. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-81.md +0 -0
  211. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-82.md +0 -0
  212. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-83.md +0 -0
  213. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-84.md +0 -0
  214. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-85.md +0 -0
  215. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-86.md +0 -0
  216. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-87.md +0 -0
  217. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-88.md +0 -0
  218. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-89.md +0 -0
  219. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-9.md +0 -0
  220. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-90.md +0 -0
  221. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-91.md +0 -0
  222. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-92.md +0 -0
  223. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-93.md +0 -0
  224. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-94.md +0 -0
  225. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-95.md +0 -0
  226. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-96.md +0 -0
  227. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-97.md +0 -0
  228. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-98.md +0 -0
  229. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/field-trials/2026-05-field-trial-99.md +0 -0
  230. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/fr/index.md +0 -0
  231. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/fr/tutorials/getting-started.md +0 -0
  232. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/add-new-domain.md +0 -0
  233. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/api-versioning.md +0 -0
  234. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/async-use-case.md +0 -0
  235. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/background-tasks.md +0 -0
  236. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/configure-auth.md +0 -0
  237. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/cors.md +0 -0
  238. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/custom-auth-middleware.md +0 -0
  239. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/decimal-unicode-input.md +0 -0
  240. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/dependency-injection.md +0 -0
  241. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/domain-events.md +0 -0
  242. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/email-address-parsing.md +0 -0
  243. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/file-upload.md +0 -0
  244. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/lifespan-and-app-state.md +0 -0
  245. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/middleware-stack.md +0 -0
  246. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/new-project.md +0 -0
  247. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/problem-details.md +0 -0
  248. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/response-patterns.md +0 -0
  249. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/run-tests.md +0 -0
  250. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/soft-delete.md +0 -0
  251. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/sqlalchemy-repository.md +0 -0
  252. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/streaming.md +0 -0
  253. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/structured-logging.md +0 -0
  254. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/validation.md +0 -0
  255. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/how-to/webhook.md +0 -0
  256. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/howto/mcp-setup.md +0 -0
  257. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/index.md +0 -0
  258. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/explanation/architecture.md +0 -0
  259. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/explanation/design-philosophy.md +0 -0
  260. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/how-to/add-new-domain.md +0 -0
  261. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/how-to/configure-auth.md +0 -0
  262. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/how-to/new-project.md +0 -0
  263. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/how-to/run-tests.md +0 -0
  264. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/how-to/sqlalchemy-repository.md +0 -0
  265. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/howto/mcp-setup.md +0 -0
  266. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/index.md +0 -0
  267. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/reference/api.md +0 -0
  268. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/reference/configuration.md +0 -0
  269. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/reference/framework-modules.md +0 -0
  270. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/tutorials/first-domain.md +0 -0
  271. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/ja/tutorials/getting-started.md +0 -0
  272. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/pt-br/index.md +0 -0
  273. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/pt-br/tutorials/getting-started.md +0 -0
  274. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/reference/api.md +0 -0
  275. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/reference/configuration.md +0 -0
  276. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/reference/framework-modules.md +0 -0
  277. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/roadmap.md +0 -0
  278. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/templates/field-trial-report.md +0 -0
  279. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/tutorials/first-domain.md +0 -0
  280. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/tutorials/getting-started.md +0 -0
  281. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/zh/index.md +0 -0
  282. {nene2_python-1.8.62 → nene2_python-1.8.64}/docs/zh/tutorials/getting-started.md +0 -0
  283. {nene2_python-1.8.62 → nene2_python-1.8.64}/package-lock.json +0 -0
  284. {nene2_python-1.8.62 → nene2_python-1.8.64}/package.json +0 -0
  285. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/__init__.py +0 -0
  286. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/__main__.py +0 -0
  287. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/app.py +0 -0
  288. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/comment/__init__.py +0 -0
  289. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/comment/entity.py +0 -0
  290. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/comment/exceptions.py +0 -0
  291. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/comment/handler.py +0 -0
  292. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/comment/repository.py +0 -0
  293. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/comment/sqlalchemy_repository.py +0 -0
  294. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/comment/use_case.py +0 -0
  295. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/mcp.py +0 -0
  296. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/note/__init__.py +0 -0
  297. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/note/async_use_case.py +0 -0
  298. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/note/entity.py +0 -0
  299. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/note/exceptions.py +0 -0
  300. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/note/handler.py +0 -0
  301. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/note/repository.py +0 -0
  302. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/note/sqlalchemy_repository.py +0 -0
  303. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/note/use_case.py +0 -0
  304. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/schema.py +0 -0
  305. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/tag/__init__.py +0 -0
  306. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/tag/entity.py +0 -0
  307. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/tag/exceptions.py +0 -0
  308. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/tag/handler.py +0 -0
  309. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/tag/repository.py +0 -0
  310. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/tag/sqlalchemy_repository.py +0 -0
  311. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/example/tag/use_case.py +0 -0
  312. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/__init__.py +0 -0
  313. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/auth/__init__.py +0 -0
  314. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/auth/api_key.py +0 -0
  315. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/auth/bearer_token.py +0 -0
  316. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/auth/deps.py +0 -0
  317. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/auth/exceptions.py +0 -0
  318. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/auth/interfaces.py +0 -0
  319. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/auth/local_verifier.py +0 -0
  320. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/cache/__init__.py +0 -0
  321. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/cache/ttl.py +0 -0
  322. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/config/__init__.py +0 -0
  323. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/config/settings.py +0 -0
  324. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/database/__init__.py +0 -0
  325. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/database/exceptions.py +0 -0
  326. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/database/health.py +0 -0
  327. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/database/interfaces.py +0 -0
  328. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/database/sqlalchemy_executor.py +0 -0
  329. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/database/utils.py +0 -0
  330. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/http/__init__.py +0 -0
  331. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/http/etag.py +0 -0
  332. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/http/health.py +0 -0
  333. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/http/pagination.py +0 -0
  334. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/http/problem_details.py +0 -0
  335. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/log/__init__.py +0 -0
  336. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/log/setup.py +0 -0
  337. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/mcp/__init__.py +0 -0
  338. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/mcp/http_client.py +0 -0
  339. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/mcp/server.py +0 -0
  340. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/middleware/__init__.py +0 -0
  341. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/middleware/domain_exception.py +0 -0
  342. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/middleware/error_handler.py +0 -0
  343. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/middleware/request_id.py +0 -0
  344. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/middleware/request_logging.py +0 -0
  345. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/middleware/request_size_limit.py +0 -0
  346. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/middleware/security_headers.py +0 -0
  347. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/middleware/setup.py +0 -0
  348. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/middleware/throttle.py +0 -0
  349. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/py.typed +0 -0
  350. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/security/__init__.py +0 -0
  351. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/security/webhook.py +0 -0
  352. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/use_case/__init__.py +0 -0
  353. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/use_case/protocols.py +0 -0
  354. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/validation/__init__.py +0 -0
  355. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/nene2/validation/exceptions.py +0 -0
  356. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/scripts/__init__.py +0 -0
  357. {nene2_python-1.8.62 → nene2_python-1.8.64}/src/scripts/export_openapi.py +0 -0
  358. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/__init__.py +0 -0
  359. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/conftest.py +0 -0
  360. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/__init__.py +0 -0
  361. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/comment/__init__.py +0 -0
  362. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/comment/test_comment_http.py +0 -0
  363. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/comment/test_comment_repository.py +0 -0
  364. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/comment/test_comment_use_case.py +0 -0
  365. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/conftest.py +0 -0
  366. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/note/__init__.py +0 -0
  367. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/note/test_async_note_use_case.py +0 -0
  368. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/note/test_list_notes.py +0 -0
  369. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/note/test_note_repository.py +0 -0
  370. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/tag/__init__.py +0 -0
  371. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/tag/test_tag_repository.py +0 -0
  372. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/tag/test_tags.py +0 -0
  373. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/test_cors.py +0 -0
  374. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/example/test_mcp.py +0 -0
  375. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/__init__.py +0 -0
  376. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/auth/__init__.py +0 -0
  377. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/auth/test_api_key.py +0 -0
  378. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/auth/test_bearer_token.py +0 -0
  379. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/auth/test_make_require_auth.py +0 -0
  380. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/auth/test_token_issuer.py +0 -0
  381. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/cache/__init__.py +0 -0
  382. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/cache/test_ttl.py +0 -0
  383. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/config/__init__.py +0 -0
  384. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/config/test_settings.py +0 -0
  385. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/database/__init__.py +0 -0
  386. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/database/test_transaction.py +0 -0
  387. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/database/test_utils.py +0 -0
  388. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/http/__init__.py +0 -0
  389. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/http/test_etag.py +0 -0
  390. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/http/test_health.py +0 -0
  391. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/http/test_pagination.py +0 -0
  392. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/http/test_problem_details.py +0 -0
  393. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/log/__init__.py +0 -0
  394. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/log/test_setup.py +0 -0
  395. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/mcp/__init__.py +0 -0
  396. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/mcp/test_http_client.py +0 -0
  397. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/mcp/test_server.py +0 -0
  398. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/middleware/__init__.py +0 -0
  399. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/middleware/test_error_handler.py +0 -0
  400. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/middleware/test_request_id.py +0 -0
  401. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/middleware/test_request_logging.py +0 -0
  402. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/middleware/test_request_size_limit.py +0 -0
  403. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/middleware/test_security_headers.py +0 -0
  404. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/middleware/test_setup_middlewares.py +0 -0
  405. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/middleware/test_simple_domain_handler.py +0 -0
  406. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/middleware/test_throttle.py +0 -0
  407. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/security/__init__.py +0 -0
  408. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/security/test_webhook.py +0 -0
  409. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/use_case/__init__.py +0 -0
  410. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/use_case/test_protocols.py +0 -0
  411. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/use_case/test_run_in_threadpool.py +0 -0
  412. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/validation/__init__.py +0 -0
  413. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/nene2/validation/test_exceptions.py +0 -0
  414. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/scripts/__init__.py +0 -0
  415. {nene2_python-1.8.62 → nene2_python-1.8.64}/tests/scripts/test_export_openapi.py +0 -0
  416. {nene2_python-1.8.62 → nene2_python-1.8.64}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nene2-python
3
- Version: 1.8.62
3
+ Version: 1.8.64
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,214 @@
1
+ # FT191: concurrent.futures モジュール
2
+
3
+ **日付**: 2026-05-21
4
+ **テーマ**: ThreadPoolExecutor / ProcessPoolExecutor / Future — 高レベル並行処理 API
5
+ **セキュリティ診断**: なし(191 % 3 = 2)
6
+
7
+ ---
8
+
9
+ ## 概要
10
+
11
+ `concurrent.futures` は threading と multiprocessing の上に薄い高レベル API を提供する stdlib モジュール。`ThreadPoolExecutor` / `ProcessPoolExecutor` を同一インターフェースで操作でき、`Future` オブジェクトで非同期タスクを管理できる。
12
+
13
+ 本 FT では `.map()` / `.submit()` / `as_completed()` / `wait()` / タイムアウト / キャンセル / エラーハンドリングを FastAPI エンドポイントから検証する。FT188(threading)・FT189(subprocess)・FT190(multiprocessing)の高レベル抽象として位置付ける。
14
+
15
+ ---
16
+
17
+ ## 実装したサンプルアプリ
18
+
19
+ **場所**: `/home/xi/docker/nene2-python-FT/ft191-concurrent-futures/`
20
+
21
+ ### 主要機能
22
+
23
+ | 関数/クラス | 概要 |
24
+ |---|---|
25
+ | `thread_pool_map(values, workers)` | ThreadPoolExecutor.map で並列二乗 |
26
+ | `thread_pool_submit(values, workers)` | ThreadPoolExecutor.submit で Future 取得 |
27
+ | `thread_as_completed(values, workers)` | as_completed で完了順に収集 |
28
+ | `thread_wait_all_completed(values, workers)` | wait(ALL_COMPLETED) |
29
+ | `thread_wait_first_completed(values, workers)` | wait(FIRST_COMPLETED) |
30
+ | `thread_wait_first_exception(values, workers)` | wait(FIRST_EXCEPTION) |
31
+ | `batch_with_error_handling(values, workers)` | 例外を握りつぶさず成功/失敗分類 |
32
+ | `process_pool_map(values, workers)` | ProcessPoolExecutor.map で CPU バウンド |
33
+ | `process_pool_submit(values, workers)` | ProcessPoolExecutor.submit |
34
+ | `process_as_completed(values, workers)` | ProcessPoolExecutor + as_completed |
35
+ | `submit_with_timeout(seconds, timeout)` | タイムアウト付き Future |
36
+ | `submit_and_cancel(values)` | キャンセル試行 |
37
+ | `thread_map_with_chunksize(values, chunksize, workers)` | チャンクサイズ指定 map |
38
+
39
+ ### HTTP エンドポイント
40
+
41
+ | メソッド | パス | 概要 |
42
+ |---|---|---|
43
+ | POST | `/futures/thread-map` | ThreadPoolExecutor.map |
44
+ | POST | `/futures/thread-submit` | submit で Future 取得 |
45
+ | POST | `/futures/thread-as-completed` | as_completed |
46
+ | POST | `/futures/thread-wait-all` | wait(ALL_COMPLETED) |
47
+ | POST | `/futures/thread-wait-first` | wait(FIRST_COMPLETED) |
48
+ | POST | `/futures/thread-wait-exception` | wait(FIRST_EXCEPTION) |
49
+ | POST | `/futures/batch-errors` | エラーハンドリング付きバッチ |
50
+ | POST | `/futures/process-map` | ProcessPoolExecutor.map |
51
+ | POST | `/futures/process-submit` | ProcessPoolExecutor.submit |
52
+ | POST | `/futures/process-as-completed` | ProcessPoolExecutor + as_completed |
53
+ | POST | `/futures/timeout` | タイムアウト付き Future |
54
+ | POST | `/futures/chunksize` | チャンクサイズ指定 |
55
+ | POST | `/futures/cancel` | キャンセル試行 |
56
+ | GET | `/futures/info` | 実行環境情報 |
57
+
58
+ ---
59
+
60
+ ## テスト結果
61
+
62
+ **51 passed**
63
+
64
+ ```
65
+ 51 passed in 10.86s
66
+ ```
67
+
68
+ ---
69
+
70
+ ## 摩擦ポイント
71
+
72
+ ### F-1: `submit_and_cancel` の戻り値型 `dict[str, int]` が `dict[str, object]` に非互換(深刻度: 低)
73
+
74
+ **事象**: `submit_and_cancel()` が `dict[str, int]` を返し、エンドポイントの戻り値型 `dict[str, object]` に対して mypy --strict が `Incompatible return value type` エラーを出した。
75
+
76
+ **原因**: mypy では `dict[str, int]` は `dict[str, object]` の部分型でない(`dict` は invariant)。TS の `Record<string, number>` を `Record<string, unknown>` に代入できないのと同じ理屈。
77
+
78
+ **対応**: `submit_and_cancel` の戻り値型を `dict[str, object]` に変更。戻り値の幅を広げても型安全性は失われない(返す値はすべて `int`)。
79
+
80
+ ---
81
+
82
+ ## 観察点
83
+
84
+ ### 観察1: ThreadPoolExecutor vs ProcessPoolExecutor の使い分け
85
+
86
+ ```python
87
+ # I/O バウンド → ThreadPoolExecutor(GIL 解放待ちの間に他スレッドが走る)
88
+ with ThreadPoolExecutor(max_workers=4) as executor:
89
+ results = list(executor.map(fetch_url, urls))
90
+
91
+ # CPU バウンド → ProcessPoolExecutor(GIL を完全に回避)
92
+ with ProcessPoolExecutor(max_workers=4) as executor:
93
+ results = list(executor.map(heavy_compute, values))
94
+ ```
95
+
96
+ threading と multiprocessing の低レベル API と同一の選択基準だが、インターフェースが統一されているため交換が容易。
97
+
98
+ ### 観察2: as_completed vs map の選択
99
+
100
+ ```python
101
+ # map: 送信順で結果が返る(遅いタスクがブロック)
102
+ results = list(executor.map(func, values))
103
+
104
+ # as_completed: 完了順で返る(高速タスクの結果を先に処理可能)
105
+ for future in as_completed(futures):
106
+ result = future.result()
107
+ process_early(result)
108
+ ```
109
+
110
+ HTTP API でストリームレスポンスを返す場合や、部分結果を早期返却する設計では `as_completed` が有利。
111
+
112
+ ### 観察3: wait() の return_when フラグ
113
+
114
+ | フラグ | 用途 |
115
+ |---|---|
116
+ | `ALL_COMPLETED` | 全タスク完了を待つ(デフォルト) |
117
+ | `FIRST_COMPLETED` | 最初のタスクが終わったら戻る |
118
+ | `FIRST_EXCEPTION` | 最初の例外発生で戻る(残タスクはキャンセルしない) |
119
+
120
+ `FIRST_EXCEPTION` は例外をすぐ検知したいが残タスクは並行継続したい場合に使う。
121
+
122
+ ### 観察4: Future.cancel() の制約
123
+
124
+ `cancel()` はタスクが**まだ実行開始されていない**場合のみ成功する。既に実行中のタスクはキャンセルできない(Python の Future はキャンセル可能 Flag のみで、OS レベルのプロセス終了は行わない)。max_workers=1 でタスクを大量投入した場合のみキャンセルが効果的。
125
+
126
+ ---
127
+
128
+ ## nene2-python フレームワークとの統合
129
+
130
+ - `ThreadPoolExecutor` は I/O バウンド UseCase(外部 API 並列呼び出し等)に適用可能。ただし FastAPI はデフォルト非同期(asyncio)であり、重い I/O は `httpx.AsyncClient` での `asyncio.gather` が自然な選択
131
+ - `ProcessPoolExecutor` は CPU バウンド変換処理(画像変換・暗号化・データ集計)を同期 UseCase として切り出す際に使う
132
+ - `max_workers` の上限制限は DoS 防止のために必須。`min(workers, MAX_WORKERS)` パターンを全関数で適用
133
+ - ProcessPoolExecutor のワーカー関数も multiprocessing と同様 pickle 可能なモジュールレベル関数に限定される(FT190 F-1 と同じ制約)
134
+
135
+ ---
136
+
137
+ ## Developer Experience (DX) Review
138
+
139
+ ### ペルソナ1: 初心者(Python 歴1年・独学中・女性・バックエンド志望)
140
+
141
+ threading/multiprocessing を学んだ後、高レベル API として concurrent.futures を使おうとしている段階。
142
+
143
+ **ドキュメント理解**: `with ThreadPoolExecutor() as executor: executor.map(func, data)` のパターンは直感的で理解しやすい。`as_completed`・`wait` は公式ドキュメントの例が豊富で困らない。`FIRST_EXCEPTION` フラグの意味は名前から推測できる。
144
+ **事故リスク**: 低。エラーハンドリングを省略すると `future.result()` で例外が再 raise されるため、未処理の例外は実行時に気づける。
145
+ **規約の使いやすさ**: `with executor:` の `with` ブロックは必須習慣で、抜け漏れ時は executor が自動終了するため安全。
146
+
147
+ ### ペルソナ2: ロースキル経験者(Python 歴3-4年・スクリプト系・男性・SES)
148
+
149
+ 既存スクリプトの `for` ループを並列化したくてコピーして使うスタイル。
150
+
151
+ **コピペ可能性**: `executor.map(func, data)` のサンプルはそのままコピーして動く。`as_completed` のパターンも明確。
152
+ **拡張時の罠**: ProcessPoolExecutor でラムダを渡すと PicklingError(FT190 F-1 の再現)。threading でも同じコードで動くため気づきにくい。
153
+ **セキュリティ的な事故リスク**: 中。`max_workers` に上限がないと DoS につながる。本実装では `MAX_WORKERS = 8` で制限。
154
+
155
+ ### ペルソナ3: フロントエンド寄り経験者(React/TS 歴4年・バックエンド転向中・ノンバイナリ)
156
+
157
+ JavaScript の `Promise.all` / `Promise.race` との比較で理解しようとしている段階。
158
+
159
+ **エラーレスポンスの質**: `batch_with_error_handling` パターンで成功/失敗を分けて返すと、クライアントが部分成功を処理しやすい。422 バリデーションエラーは自動返却される。
160
+ **Python 固有概念の学習コスト**: `Future` は JS の `Promise` に近い。`as_completed` は `Promise.race` の複数解決版として理解できる。`wait(FIRST_COMPLETED)` が `Promise.race`、`wait(ALL_COMPLETED)` が `Promise.all` に相当する。
161
+ **事故リスク**: 低。HTTP 入力のバリデーションが Pydantic で保護されている。
162
+
163
+ ### ペルソナ4: バックエンド経験者(Django/FastAPI 歴5-6年・男性・リードエンジニア)
164
+
165
+ asyncio vs concurrent.futures の使い分けを判断する立場。
166
+
167
+ **他フレームワークとの差異**: FastAPI は async/await が基本なので、I/O 並列は `asyncio.gather` が自然な選択。`concurrent.futures` は CPU バウンドと、非同期対応していないレガシーライブラリの同期 I/O をスレッドプールで包む用途に限定される。`loop.run_in_executor()` で asyncio と統合できる。
168
+ **nene2-python の薄さへの評価**: UseCase 層が HTTP 非依存なので、`ThreadPoolExecutor` を UseCase 内で直接使う設計も許容される。ただし `asyncio` 移行を前提とする場合は技術的負債になりやすい。
169
+ **本番投入可能性**: チームが asyncio に慣れているなら concurrent.futures は補助的な役割に留めるべき。混在するとコードの可読性が下がる。
170
+
171
+ ### ペルソナ5: シニアエンジニア(設計・コードレビュー担当・女性・10-12年)
172
+
173
+ コードレビューで concurrent.futures の誤用を検出する立場。
174
+
175
+ **コードレビューチェックポイント**:
176
+ - [ ] `max_workers` に上限制限があるか(無制限はリソース枯渇)
177
+ - [ ] `with executor:` のコンテキストマネージャーを使っているか(`executor.shutdown()` の漏れ防止)
178
+ - [ ] ProcessPoolExecutor のワーカー関数がモジュールレベルか(PicklingError 防止)
179
+ - [ ] `future.result()` の例外ハンドリングが書かれているか(未処理は実行時エラーが伝播する)
180
+ - [ ] タイムアウトが指定されているか(`future.result(timeout=N)` や `wait(timeout=N)`)
181
+
182
+ **チームでの安全な共有パターン**: ワーカー関数を `_workers.py` に分離する規則を設けると、pickle 可能性と単体テスト可能性が高まる。
183
+ **ツール追加の必要性**: なし(ruff には concurrent.futures 固有の追加ルールはない)。
184
+
185
+ ### ペルソナ6: 設計者・ポリシー照合(nene2-python 設計ポリシー目線)
186
+
187
+ CLAUDE.md ポリシーとの整合性を確認する。
188
+
189
+ **ポリシー達成度**: 高
190
+ **「初心者でも安全な API」達成度**: 高(`with executor:` がリソースリーク防止を保証、HTTP 境界の Pydantic バリデーションで DoS 制限)
191
+ **設計上の負債・ドキュメント不足**: `asyncio` と concurrent.futures の使い分けガイドが CLAUDE.md に不足。FastAPI アプリでは asyncio が優先される旨を追記する価値がある。
192
+ **Follow-up Issues**: なし(即時対応済み)
193
+
194
+ ---
195
+
196
+ ## Follow-up Issues
197
+
198
+ ### 即時対応済み
199
+
200
+ | 対応内容 | 対応方法 |
201
+ |---|---|
202
+ | `dict[str, int]` を `dict[str, object]` に変更(F-1) | `submit_and_cancel` の戻り値型を修正 |
203
+
204
+ ### 新規 Issue
205
+
206
+ なし(セキュリティ診断なし、全問題は即時解決済み)
207
+
208
+ ---
209
+
210
+ ## まとめ
211
+
212
+ concurrent.futures の主要パターン(ThreadPoolExecutor / ProcessPoolExecutor・submit / map / as_completed / wait・タイムアウト・キャンセル・エラーハンドリング)を 14 エンドポイント・51 テストで検証した。FT191 固有の発見は 1 点: `dict[str, int]` → `dict[str, object]` の invariant 問題(mypy --strict 即時検出)。
213
+
214
+ threading(FT188)・multiprocessing(FT190)の高レベル API として concurrent.futures は使いやすく、処理系(スレッド/プロセス)の交換コストが低い。FastAPI + asyncio 環境では補助的な位置付けになるが、CPU バウンド処理のオフロードには有効。