nene2-python 1.8.51__tar.gz → 1.8.52__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 (403) hide show
  1. {nene2_python-1.8.51 → nene2_python-1.8.52}/PKG-INFO +1 -1
  2. nene2_python-1.8.52/docs/field-trials/2026-05-field-trial-181.md +269 -0
  3. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/INDEX.md +3 -2
  4. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/todo/current.md +5 -4
  5. {nene2_python-1.8.51 → nene2_python-1.8.52}/pyproject.toml +1 -1
  6. {nene2_python-1.8.51 → nene2_python-1.8.52}/.env.example +0 -0
  7. {nene2_python-1.8.51 → nene2_python-1.8.52}/.github/workflows/ci.yml +0 -0
  8. {nene2_python-1.8.51 → nene2_python-1.8.52}/.github/workflows/docs.yml +0 -0
  9. {nene2_python-1.8.51 → nene2_python-1.8.52}/.github/workflows/publish.yml +0 -0
  10. {nene2_python-1.8.51 → nene2_python-1.8.52}/.gitignore +0 -0
  11. {nene2_python-1.8.51 → nene2_python-1.8.52}/.vitepress/config.mts +0 -0
  12. {nene2_python-1.8.51 → nene2_python-1.8.52}/.vitepress/theme/custom.css +0 -0
  13. {nene2_python-1.8.51 → nene2_python-1.8.52}/.vitepress/theme/index.ts +0 -0
  14. {nene2_python-1.8.51 → nene2_python-1.8.52}/AGENTS.md +0 -0
  15. {nene2_python-1.8.51 → nene2_python-1.8.52}/CHANGELOG.md +0 -0
  16. {nene2_python-1.8.51 → nene2_python-1.8.52}/CLAUDE.md +0 -0
  17. {nene2_python-1.8.51 → nene2_python-1.8.52}/Dockerfile +0 -0
  18. {nene2_python-1.8.51 → nene2_python-1.8.52}/LICENSE +0 -0
  19. {nene2_python-1.8.51 → nene2_python-1.8.52}/README.md +0 -0
  20. {nene2_python-1.8.51 → nene2_python-1.8.52}/alembic/README +0 -0
  21. {nene2_python-1.8.51 → nene2_python-1.8.52}/alembic/env.py +0 -0
  22. {nene2_python-1.8.51 → nene2_python-1.8.52}/alembic/script.py.mako +0 -0
  23. {nene2_python-1.8.51 → nene2_python-1.8.52}/alembic/versions/001_create_notes_and_tags_tables.py +0 -0
  24. {nene2_python-1.8.51 → nene2_python-1.8.52}/alembic.ini +0 -0
  25. {nene2_python-1.8.51 → nene2_python-1.8.52}/compose.yaml +0 -0
  26. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/adr/0001-toolchain.md +0 -0
  27. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/adr/0002-clean-architecture.md +0 -0
  28. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/adr/0003-security-first.md +0 -0
  29. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/adr/0004-ai-first-design.md +0 -0
  30. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/adr/0005-logging.md +0 -0
  31. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/adr/0006-rate-limiting.md +0 -0
  32. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/adr/0009-mcp-design.md +0 -0
  33. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/adr/0010-async-use-case.md +0 -0
  34. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/adr/0011-mcp-as-core-dependency.md +0 -0
  35. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/de/index.md +0 -0
  36. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/de/tutorials/getting-started.md +0 -0
  37. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/explanation/architecture.md +0 -0
  38. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/explanation/design-philosophy.md +0 -0
  39. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-1.md +0 -0
  40. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-10.md +0 -0
  41. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-100.md +0 -0
  42. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-101.md +0 -0
  43. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-102.md +0 -0
  44. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-103.md +0 -0
  45. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-104.md +0 -0
  46. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-105.md +0 -0
  47. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-106.md +0 -0
  48. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-107.md +0 -0
  49. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-108.md +0 -0
  50. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-109.md +0 -0
  51. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-11.md +0 -0
  52. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-110.md +0 -0
  53. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-111.md +0 -0
  54. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-112.md +0 -0
  55. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-113.md +0 -0
  56. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-114.md +0 -0
  57. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-115.md +0 -0
  58. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-116.md +0 -0
  59. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-117.md +0 -0
  60. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-118.md +0 -0
  61. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-119.md +0 -0
  62. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-12.md +0 -0
  63. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-120.md +0 -0
  64. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-121.md +0 -0
  65. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-122.md +0 -0
  66. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-123.md +0 -0
  67. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-124.md +0 -0
  68. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-125.md +0 -0
  69. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-126.md +0 -0
  70. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-127.md +0 -0
  71. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-128.md +0 -0
  72. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-129.md +0 -0
  73. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-13.md +0 -0
  74. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-130.md +0 -0
  75. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-131.md +0 -0
  76. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-132.md +0 -0
  77. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-133.md +0 -0
  78. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-134.md +0 -0
  79. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-135.md +0 -0
  80. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-136.md +0 -0
  81. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-137.md +0 -0
  82. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-138.md +0 -0
  83. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-139.md +0 -0
  84. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-14.md +0 -0
  85. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-140.md +0 -0
  86. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-141.md +0 -0
  87. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-142.md +0 -0
  88. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-143.md +0 -0
  89. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-144.md +0 -0
  90. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-145.md +0 -0
  91. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-146.md +0 -0
  92. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-147.md +0 -0
  93. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-148.md +0 -0
  94. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-149.md +0 -0
  95. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-15.md +0 -0
  96. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-150.md +0 -0
  97. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-151.md +0 -0
  98. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-152.md +0 -0
  99. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-153.md +0 -0
  100. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-154.md +0 -0
  101. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-155.md +0 -0
  102. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-156.md +0 -0
  103. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-157.md +0 -0
  104. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-158.md +0 -0
  105. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-159.md +0 -0
  106. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-16.md +0 -0
  107. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-160.md +0 -0
  108. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-161.md +0 -0
  109. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-162.md +0 -0
  110. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-163.md +0 -0
  111. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-164.md +0 -0
  112. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-165.md +0 -0
  113. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-166.md +0 -0
  114. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-167.md +0 -0
  115. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-168.md +0 -0
  116. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-169.md +0 -0
  117. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-17.md +0 -0
  118. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-170.md +0 -0
  119. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-171.md +0 -0
  120. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-172.md +0 -0
  121. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-173.md +0 -0
  122. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-174.md +0 -0
  123. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-175.md +0 -0
  124. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-176.md +0 -0
  125. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-177.md +0 -0
  126. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-178.md +0 -0
  127. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-179.md +0 -0
  128. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-18.md +0 -0
  129. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-180.md +0 -0
  130. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-19.md +0 -0
  131. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-2.md +0 -0
  132. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-20.md +0 -0
  133. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-21.md +0 -0
  134. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-22.md +0 -0
  135. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-23.md +0 -0
  136. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-24.md +0 -0
  137. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-25.md +0 -0
  138. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-26.md +0 -0
  139. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-27.md +0 -0
  140. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-28.md +0 -0
  141. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-29.md +0 -0
  142. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-3.md +0 -0
  143. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-30.md +0 -0
  144. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-31.md +0 -0
  145. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-32.md +0 -0
  146. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-33.md +0 -0
  147. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-34.md +0 -0
  148. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-35.md +0 -0
  149. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-36.md +0 -0
  150. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-37.md +0 -0
  151. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-38.md +0 -0
  152. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-39.md +0 -0
  153. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-4.md +0 -0
  154. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-40.md +0 -0
  155. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-41.md +0 -0
  156. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-42.md +0 -0
  157. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-43.md +0 -0
  158. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-44.md +0 -0
  159. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-45.md +0 -0
  160. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-46.md +0 -0
  161. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-47.md +0 -0
  162. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-48.md +0 -0
  163. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-49.md +0 -0
  164. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-5.md +0 -0
  165. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-50.md +0 -0
  166. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-51.md +0 -0
  167. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-52.md +0 -0
  168. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-53.md +0 -0
  169. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-54.md +0 -0
  170. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-55.md +0 -0
  171. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-56.md +0 -0
  172. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-57.md +0 -0
  173. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-58.md +0 -0
  174. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-59.md +0 -0
  175. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-6.md +0 -0
  176. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-60.md +0 -0
  177. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-61.md +0 -0
  178. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-62.md +0 -0
  179. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-63.md +0 -0
  180. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-64.md +0 -0
  181. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-65.md +0 -0
  182. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-66.md +0 -0
  183. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-67.md +0 -0
  184. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-68.md +0 -0
  185. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-69.md +0 -0
  186. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-7.md +0 -0
  187. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-70.md +0 -0
  188. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-71.md +0 -0
  189. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-72.md +0 -0
  190. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-73.md +0 -0
  191. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-74.md +0 -0
  192. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-75.md +0 -0
  193. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-76.md +0 -0
  194. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-77.md +0 -0
  195. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-78.md +0 -0
  196. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-79.md +0 -0
  197. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-8.md +0 -0
  198. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-80.md +0 -0
  199. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-81.md +0 -0
  200. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-82.md +0 -0
  201. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-83.md +0 -0
  202. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-84.md +0 -0
  203. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-85.md +0 -0
  204. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-86.md +0 -0
  205. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-87.md +0 -0
  206. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-88.md +0 -0
  207. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-89.md +0 -0
  208. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-9.md +0 -0
  209. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-90.md +0 -0
  210. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-91.md +0 -0
  211. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-92.md +0 -0
  212. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-93.md +0 -0
  213. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-94.md +0 -0
  214. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-95.md +0 -0
  215. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-96.md +0 -0
  216. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-97.md +0 -0
  217. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-98.md +0 -0
  218. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/field-trials/2026-05-field-trial-99.md +0 -0
  219. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/fr/index.md +0 -0
  220. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/fr/tutorials/getting-started.md +0 -0
  221. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/how-to/add-new-domain.md +0 -0
  222. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/how-to/api-versioning.md +0 -0
  223. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/how-to/async-use-case.md +0 -0
  224. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/how-to/background-tasks.md +0 -0
  225. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/how-to/configure-auth.md +0 -0
  226. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/how-to/cors.md +0 -0
  227. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/how-to/custom-auth-middleware.md +0 -0
  228. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/how-to/dependency-injection.md +0 -0
  229. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/how-to/domain-events.md +0 -0
  230. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/how-to/file-upload.md +0 -0
  231. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/how-to/lifespan-and-app-state.md +0 -0
  232. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/how-to/middleware-stack.md +0 -0
  233. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/how-to/new-project.md +0 -0
  234. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/how-to/problem-details.md +0 -0
  235. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/how-to/response-patterns.md +0 -0
  236. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/how-to/run-tests.md +0 -0
  237. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/how-to/soft-delete.md +0 -0
  238. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/how-to/sqlalchemy-repository.md +0 -0
  239. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/how-to/streaming.md +0 -0
  240. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/how-to/structured-logging.md +0 -0
  241. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/how-to/validation.md +0 -0
  242. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/how-to/webhook.md +0 -0
  243. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/howto/mcp-setup.md +0 -0
  244. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/index.md +0 -0
  245. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/ja/explanation/architecture.md +0 -0
  246. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/ja/explanation/design-philosophy.md +0 -0
  247. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/ja/how-to/add-new-domain.md +0 -0
  248. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/ja/how-to/configure-auth.md +0 -0
  249. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/ja/how-to/new-project.md +0 -0
  250. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/ja/how-to/run-tests.md +0 -0
  251. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/ja/how-to/sqlalchemy-repository.md +0 -0
  252. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/ja/howto/mcp-setup.md +0 -0
  253. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/ja/index.md +0 -0
  254. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/ja/reference/api.md +0 -0
  255. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/ja/reference/configuration.md +0 -0
  256. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/ja/reference/framework-modules.md +0 -0
  257. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/ja/tutorials/first-domain.md +0 -0
  258. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/ja/tutorials/getting-started.md +0 -0
  259. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/pt-br/index.md +0 -0
  260. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/pt-br/tutorials/getting-started.md +0 -0
  261. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/reference/api.md +0 -0
  262. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/reference/configuration.md +0 -0
  263. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/reference/framework-modules.md +0 -0
  264. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/roadmap.md +0 -0
  265. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/templates/field-trial-report.md +0 -0
  266. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/tutorials/first-domain.md +0 -0
  267. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/tutorials/getting-started.md +0 -0
  268. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/zh/index.md +0 -0
  269. {nene2_python-1.8.51 → nene2_python-1.8.52}/docs/zh/tutorials/getting-started.md +0 -0
  270. {nene2_python-1.8.51 → nene2_python-1.8.52}/package-lock.json +0 -0
  271. {nene2_python-1.8.51 → nene2_python-1.8.52}/package.json +0 -0
  272. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/__init__.py +0 -0
  273. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/__main__.py +0 -0
  274. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/app.py +0 -0
  275. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/comment/__init__.py +0 -0
  276. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/comment/entity.py +0 -0
  277. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/comment/exceptions.py +0 -0
  278. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/comment/handler.py +0 -0
  279. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/comment/repository.py +0 -0
  280. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/comment/sqlalchemy_repository.py +0 -0
  281. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/comment/use_case.py +0 -0
  282. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/mcp.py +0 -0
  283. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/note/__init__.py +0 -0
  284. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/note/async_use_case.py +0 -0
  285. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/note/entity.py +0 -0
  286. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/note/exceptions.py +0 -0
  287. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/note/handler.py +0 -0
  288. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/note/repository.py +0 -0
  289. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/note/sqlalchemy_repository.py +0 -0
  290. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/note/use_case.py +0 -0
  291. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/schema.py +0 -0
  292. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/tag/__init__.py +0 -0
  293. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/tag/entity.py +0 -0
  294. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/tag/exceptions.py +0 -0
  295. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/tag/handler.py +0 -0
  296. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/tag/repository.py +0 -0
  297. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/tag/sqlalchemy_repository.py +0 -0
  298. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/example/tag/use_case.py +0 -0
  299. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/__init__.py +0 -0
  300. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/auth/__init__.py +0 -0
  301. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/auth/api_key.py +0 -0
  302. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/auth/bearer_token.py +0 -0
  303. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/auth/deps.py +0 -0
  304. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/auth/exceptions.py +0 -0
  305. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/auth/interfaces.py +0 -0
  306. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/auth/local_verifier.py +0 -0
  307. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/cache/__init__.py +0 -0
  308. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/cache/ttl.py +0 -0
  309. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/config/__init__.py +0 -0
  310. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/config/settings.py +0 -0
  311. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/database/__init__.py +0 -0
  312. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/database/exceptions.py +0 -0
  313. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/database/health.py +0 -0
  314. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/database/interfaces.py +0 -0
  315. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/database/sqlalchemy_executor.py +0 -0
  316. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/database/utils.py +0 -0
  317. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/http/__init__.py +0 -0
  318. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/http/etag.py +0 -0
  319. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/http/health.py +0 -0
  320. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/http/pagination.py +0 -0
  321. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/http/problem_details.py +0 -0
  322. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/log/__init__.py +0 -0
  323. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/log/setup.py +0 -0
  324. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/mcp/__init__.py +0 -0
  325. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/mcp/http_client.py +0 -0
  326. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/mcp/server.py +0 -0
  327. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/middleware/__init__.py +0 -0
  328. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/middleware/domain_exception.py +0 -0
  329. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/middleware/error_handler.py +0 -0
  330. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/middleware/request_id.py +0 -0
  331. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/middleware/request_logging.py +0 -0
  332. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/middleware/request_size_limit.py +0 -0
  333. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/middleware/security_headers.py +0 -0
  334. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/middleware/setup.py +0 -0
  335. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/middleware/throttle.py +0 -0
  336. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/py.typed +0 -0
  337. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/security/__init__.py +0 -0
  338. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/security/webhook.py +0 -0
  339. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/use_case/__init__.py +0 -0
  340. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/use_case/protocols.py +0 -0
  341. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/validation/__init__.py +0 -0
  342. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/nene2/validation/exceptions.py +0 -0
  343. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/scripts/__init__.py +0 -0
  344. {nene2_python-1.8.51 → nene2_python-1.8.52}/src/scripts/export_openapi.py +0 -0
  345. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/__init__.py +0 -0
  346. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/conftest.py +0 -0
  347. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/example/__init__.py +0 -0
  348. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/example/comment/__init__.py +0 -0
  349. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/example/comment/test_comment_http.py +0 -0
  350. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/example/comment/test_comment_repository.py +0 -0
  351. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/example/comment/test_comment_use_case.py +0 -0
  352. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/example/conftest.py +0 -0
  353. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/example/note/__init__.py +0 -0
  354. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/example/note/test_async_note_use_case.py +0 -0
  355. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/example/note/test_list_notes.py +0 -0
  356. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/example/note/test_note_repository.py +0 -0
  357. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/example/tag/__init__.py +0 -0
  358. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/example/tag/test_tag_repository.py +0 -0
  359. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/example/tag/test_tags.py +0 -0
  360. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/example/test_cors.py +0 -0
  361. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/example/test_mcp.py +0 -0
  362. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/__init__.py +0 -0
  363. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/auth/__init__.py +0 -0
  364. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/auth/test_api_key.py +0 -0
  365. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/auth/test_bearer_token.py +0 -0
  366. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/auth/test_make_require_auth.py +0 -0
  367. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/auth/test_token_issuer.py +0 -0
  368. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/cache/__init__.py +0 -0
  369. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/cache/test_ttl.py +0 -0
  370. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/config/__init__.py +0 -0
  371. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/config/test_settings.py +0 -0
  372. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/database/__init__.py +0 -0
  373. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/database/test_transaction.py +0 -0
  374. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/database/test_utils.py +0 -0
  375. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/http/__init__.py +0 -0
  376. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/http/test_etag.py +0 -0
  377. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/http/test_health.py +0 -0
  378. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/http/test_pagination.py +0 -0
  379. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/http/test_problem_details.py +0 -0
  380. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/log/__init__.py +0 -0
  381. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/log/test_setup.py +0 -0
  382. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/mcp/__init__.py +0 -0
  383. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/mcp/test_http_client.py +0 -0
  384. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/mcp/test_server.py +0 -0
  385. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/middleware/__init__.py +0 -0
  386. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/middleware/test_error_handler.py +0 -0
  387. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/middleware/test_request_id.py +0 -0
  388. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/middleware/test_request_logging.py +0 -0
  389. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/middleware/test_request_size_limit.py +0 -0
  390. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/middleware/test_security_headers.py +0 -0
  391. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/middleware/test_setup_middlewares.py +0 -0
  392. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/middleware/test_simple_domain_handler.py +0 -0
  393. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/middleware/test_throttle.py +0 -0
  394. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/security/__init__.py +0 -0
  395. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/security/test_webhook.py +0 -0
  396. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/use_case/__init__.py +0 -0
  397. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/use_case/test_protocols.py +0 -0
  398. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/use_case/test_run_in_threadpool.py +0 -0
  399. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/validation/__init__.py +0 -0
  400. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/nene2/validation/test_exceptions.py +0 -0
  401. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/scripts/__init__.py +0 -0
  402. {nene2_python-1.8.51 → nene2_python-1.8.52}/tests/scripts/test_export_openapi.py +0 -0
  403. {nene2_python-1.8.51 → nene2_python-1.8.52}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nene2-python
3
- Version: 1.8.51
3
+ Version: 1.8.52
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,269 @@
1
+ # FT181: gzip モジュール
2
+
3
+ **日付**: 2026-05-21
4
+ **テーマ**: gzip 圧縮・解凍・メタデータ操作・ビルド再現性
5
+ **セキュリティ診断**: なし(181 % 3 = 1)
6
+
7
+ ---
8
+
9
+ ## 概要
10
+
11
+ Python 標準ライブラリの `gzip` モジュールを検証する。
12
+ FT179(zlib)との違いを意識しながら、gzip 固有のヘッダーメタデータ(ファイル名・mtime)、
13
+ 展開爆弾対策付きのストリーミング解凍、mtime=0 による決定論的圧縮(ビルド再現性)まで網羅する。
14
+
15
+ ---
16
+
17
+ ## 実装したサンプルアプリ
18
+
19
+ **場所**: `/home/xi/docker/nene2-python-FT/ft181-gzip/`
20
+
21
+ ### 主要機能
22
+
23
+ | 関数/クラス | 概要 |
24
+ |---|---|
25
+ | `compress(data, filename, level, mtime)` | gzip 圧縮(`CompressResult` 返却) |
26
+ | `compress_to_bytes(data, filename, level, mtime)` | gzip 圧縮して raw bytes を返す |
27
+ | `decompress(compressed)` | 展開爆弾対策付きストリーミング解凍(`DecompressResult` 返却) |
28
+ | `decompress_to_bytes(compressed)` | 解凍して raw bytes を返す |
29
+ | `_parse_gzip_header(data)` | gzip ヘッダーからファイル名・mtime を手動解析 |
30
+ | `read_gzip_info(compressed)` | メタデータ読み取り(`GzipInfo` 返却) |
31
+ | `roundtrip(data, level)` | 圧縮 → 解凍のラウンドトリップ検証 |
32
+ | `compress_streaming(chunks, level)` | チャンクリストのストリーミング圧縮 |
33
+ | `compress_deterministic(data, level)` | mtime=0 で決定論的圧縮 |
34
+ | `is_reproducible(data, level)` | 2 回圧縮して同一バイト列か確認 |
35
+
36
+ ### HTTP エンドポイント
37
+
38
+ | メソッド | パス | 概要 |
39
+ |---|---|---|
40
+ | POST | `/compress` | gzip 圧縮(filename/level 指定可) |
41
+ | POST | `/decompress` | gzip 解凍 |
42
+ | POST | `/info` | gzip メタデータ読み取り |
43
+ | POST | `/roundtrip` | ラウンドトリップ検証 |
44
+ | POST | `/compress/streaming` | ストリーミング gzip 圧縮 |
45
+ | POST | `/compress/deterministic` | 決定論的圧縮(mtime=0) |
46
+
47
+ ---
48
+
49
+ ## テスト結果
50
+
51
+ **40 passed**(初回 33 通過 → F-1 修正後 40 全通過)
52
+
53
+ ```
54
+ 40 passed in 0.60s
55
+ ```
56
+
57
+ mypy: Success / ruff: All checks passed / pip-audit: PYSEC-2025-183(継続監視)
58
+
59
+ ---
60
+
61
+ ## 摩擦ポイント
62
+
63
+ ### F-1: `GzipFile.name` は `fileobj=BytesIO` のとき常に空文字列を返す(深刻度: 中)
64
+
65
+ **事象**: `gzip.GzipFile(filename="test.txt", mode="wb", fileobj=BytesIO())` で
66
+ ファイル名を指定して圧縮しても、読み取り時に `gz.name` が `""` を返す。
67
+
68
+ **原因**: Python の `GzipFile.name` プロパティはファイルシステム上のパスを返す設計。
69
+ `fileobj` が `BytesIO` の場合、ディスク上のファイルパスが存在しないため常に空文字列になる。
70
+ ファイル名はバイト列として gzip ヘッダーの FNAME フィールドに書き込まれているが、
71
+ `gz.name` 経由では取得できない。
72
+
73
+ **対応**: gzip ヘッダーを直接解析する `_parse_gzip_header()` を実装:
74
+ ```python
75
+ _GZIP_MAGIC = b"\x1f\x8b"
76
+ _FEXTRA = 4
77
+ _FNAME = 8
78
+
79
+ def _parse_gzip_header(data: bytes) -> tuple[str, int] | None:
80
+ if len(data) < 10 or data[:2] != _GZIP_MAGIC:
81
+ return None
82
+ flags = data[3]
83
+ mtime = int.from_bytes(data[4:8], "little")
84
+ offset = 10
85
+ if flags & _FEXTRA:
86
+ xlen = int.from_bytes(data[offset:offset+2], "little")
87
+ offset += 2 + xlen
88
+ filename = ""
89
+ if flags & _FNAME:
90
+ end = data.find(b"\x00", offset)
91
+ filename = data[offset:end].decode("latin-1")
92
+ return filename, mtime
93
+ ```
94
+
95
+ **副次効果**: 不正な gzip データ(マジックバイト不一致)を早期に拒否できるため、
96
+ `read_gzip_info()` の invalid data 検出も正確になった。
97
+
98
+ ---
99
+
100
+ ## 観察点
101
+
102
+ ### 観察1: gzip = zlib + ヘッダーメタデータ
103
+
104
+ zlib(FT179)との主な違いは gzip ヘッダーの存在。
105
+
106
+ | 特性 | zlib | gzip |
107
+ |---|---|---|
108
+ | ファイルフォーマット | raw deflate + zlib wrapper | deflate + gzip header + trailer |
109
+ | ファイル名 | なし | FNAME フィールドに格納 |
110
+ | mtime | なし | MTIME フィールドに格納 |
111
+ | マジックバイト | なし | `\x1f\x8b` |
112
+ | 用途 | ネットワーク圧縮・インメモリ | .gz ファイル、HTTP Content-Encoding |
113
+
114
+ HTTP の `Content-Encoding: gzip` は gzip フォーマット、
115
+ `Transfer-Encoding: chunked` と組み合わせるストリーミングは zlib が適している。
116
+
117
+ ### 観察2: mtime=0 でビルド再現性を確保できる
118
+
119
+ gzip ヘッダーには圧縮時刻(mtime)が埋め込まれる。
120
+ mtime がデフォルト(`time.time()`)の場合、同一データでも圧縮のたびにバイト列が変わる。
121
+
122
+ ```python
123
+ # 問題: mtime が異なると出力が変わる → Docker イメージのレイヤーハッシュが毎回変わる
124
+ buf1 = io.BytesIO()
125
+ with gzip.GzipFile(mode="wb", fileobj=buf1, mtime=time.time()) as gz:
126
+ gz.write(data)
127
+ # buf1.getvalue() != buf2.getvalue()(別の時刻で生成)
128
+
129
+ # 解決: mtime=0 で決定論的
130
+ with gzip.GzipFile(mode="wb", fileobj=buf, mtime=0.0) as gz:
131
+ gz.write(data)
132
+ # 同一データ → 常に同一バイト列
133
+ ```
134
+
135
+ Docker イメージビルドや CI でのアーティファクトハッシュ検証に重要。
136
+
137
+ ### 観察3: `GzipFile` の `mode` パラメータは省略できない
138
+
139
+ ```python
140
+ # 正: mode を明示
141
+ gzip.GzipFile(fileobj=buf, mode="rb")
142
+
143
+ # 誤: mode 省略 → TypeError または予期しない動作
144
+ gzip.GzipFile(fileobj=buf)
145
+ ```
146
+
147
+ `open()` とは異なり `mode` のデフォルト値が `"rb"` になっている(Python 3.12 で確認)が、
148
+ 明示指定するほうが意図が明確で安全。
149
+
150
+ ### 観察4: 展開爆弾対策は zlib と同様にストリーミング読み取りが必須
151
+
152
+ ```python
153
+ # 危険: gzip.decompress() は全データをメモリに展開する
154
+ gzip.decompress(huge_gzip) # → OOM の危険
155
+
156
+ # 安全: チャンク読み取りで MAX_OUTPUT_BYTES を監視
157
+ with gzip.GzipFile(fileobj=buf, mode="rb") as gz:
158
+ while True:
159
+ chunk = gz.read(CHUNK_SIZE)
160
+ total += len(chunk)
161
+ if total > MAX_OUTPUT_BYTES:
162
+ return None # 早期終了
163
+ ```
164
+
165
+ ---
166
+
167
+ ## nene2-python フレームワークとの統合
168
+
169
+ - `compress_to_bytes(data, filename="...", mtime=0.0)` はファイルアップロード API のレスポンスに直接使える
170
+ - `_parse_gzip_header()` はヘッダー検証のユーティリティとして再利用可能
171
+ - `MAX_OUTPUT_BYTES = 50MB` の展開爆弾対策は zlib(FT179)と同じ定数で統一
172
+ - Pydantic `max_length=20_971_520` で 10MB のデータ(hex 換算 20MB)まで制限
173
+ - `compress_deterministic()` は CI アーティファクトのハッシュ検証に使える
174
+
175
+ ---
176
+
177
+ ## Developer Experience (DX) Review
178
+
179
+ ### ペルソナ1: 初心者(Python 歴1年・独学中・女性・バックエンド志望)
180
+
181
+ API レスポンスのデータを gzip 圧縮して返す機能を実装しようとしている。
182
+
183
+ **ドキュメント理解**: `gzip.compress()` / `gzip.decompress()` はシンプルで分かりやすい。
184
+ `GzipFile` クラスの `filename` / `mtime` パラメータの意味は公式ドキュメントに書いてあるが、
185
+ なぜ `gz.name` で取り戻せないかは書いていない(F-1 の罠)。
186
+ **事故リスク**: 高。`gzip.decompress()` に信頼できないデータを渡すと OOM になりうる。
187
+ (zlib FT179 と同じリスク)
188
+ **規約の使いやすさ**: `gzip.open()` は通常のファイル操作に慣れた人には直感的。
189
+ `io.BytesIO` との組み合わせはやや初心者にはハードルがある。
190
+
191
+ ### ペルソナ2: ロースキル経験者(Python 歴3-4年・スクリプト系・男性・SES)
192
+
193
+ 既存の gzip ファイル処理コードをコピーして API に組み込もうとしている。
194
+
195
+ **コピペ可能性**: `gzip.compress()` / `gzip.decompress()` はスクリプト系コードにもよく登場するが、
196
+ 展開爆弾対策を含むパターンは少ない。コピペ元次第でセキュリティが変わる。
197
+ **拡張時の罠**: `compress_to_bytes()` の `mtime=0.0` のデフォルトを削除すると
198
+ ビルド再現性が失われるが、エラーは出ない。「意味があるから付けてある」と分かりにくい。
199
+ **セキュリティ的な事故リスク**: 高。展開爆弾対策なしは DoS に直結。
200
+
201
+ ### ペルソナ3: フロントエンド寄り経験者(React/TS 歴4年・バックエンド転向中・ノンバイナリ)
202
+
203
+ フロントエンドから `.gz` ファイルを送信するアップロード API を実装している。
204
+
205
+ **エラーレスポンスの質**: 不正な gzip データに 400 を返す設計は良い。
206
+ `gzip.BadGzipFile` を catch して適切なエラーメッセージを返すことで、
207
+ クライアント側でのデバッグが容易。
208
+ **Python 固有概念の学習コスト**: `io.BytesIO` を使ったインメモリ gzip 操作は、
209
+ JS の `Blob` / `ArrayBuffer` に慣れた人には少し概念が違う。
210
+ **事故リスク**: 低。HTTP 境界での Pydantic バリデーションが充実。
211
+
212
+ ### ペルソナ4: バックエンド経験者(Django/FastAPI 歴5-6年・男性・リードエンジニア)
213
+
214
+ HTTP レスポンスの `Content-Encoding: gzip` や静的ファイルの事前圧縮を検討している。
215
+
216
+ **他フレームワークとの差異**: Django の `GZipMiddleware` は透過的に圧縮するため、
217
+ gzip を手動で操作することは少ない。FastAPI でも `GZipMiddleware` が利用可能。
218
+ ただし事前圧縮(pre-compressed static files)や S3 アップロード前圧縮には直接操作が必要。
219
+ **nene2-python の薄さへの評価**: `compress_deterministic()` の mtime=0 パターンは
220
+ CI/CD パイプラインでよく使うが、stdlib ドキュメントには目立たない。
221
+ フレームワーク側で「ビルド再現性のある圧縮」を推奨パターンとして提示する価値がある。
222
+ **本番投入可能性**: 展開爆弾対策 + ヘッダー手動解析は本番品質。
223
+
224
+ ### ペルソナ5: シニアエンジニア(設計・コードレビュー担当・女性・10-12年)
225
+
226
+ **コードレビューチェックポイント**:
227
+ - [x] `gzip.decompress()` 直接使用でなく、ストリーミング解凍で上限チェックがあるか
228
+ - [x] `gz.name` でファイル名を取得しようとして空文字列を返すコードがないか(F-1)
229
+ - [x] `mtime` が指定されていないコードが「毎回違うバイト列」を生成していないか
230
+
231
+ **チームでの安全なパターン**: `compress_to_bytes(data, mtime=0.0)` を社内標準として
232
+ 「再現可能圧縮はこれを使う」とドキュメント化することで、mtime の罠を全員が回避できる。
233
+ **ツール追加の必要性**: bandit B301 相当(`gzip.decompress` 直接使用)を
234
+ コードレビューチェックリストに追加すべき。ruff には相当ルールなし。
235
+
236
+ ### ペルソナ6: 設計者・ポリシー照合(nene2-python 設計ポリシー目線)
237
+
238
+ **ポリシー達成度**: 高
239
+ **「初心者でも安全な API」達成度**: 中
240
+ — `gzip.decompress()` 直接使用の罠は FT179 と同じ。
241
+ zlib と gzip で同じ問題が繰り返されることは「stdlib の設計の一貫性のなさ」を示している。
242
+ **設計上の負債**: `_parse_gzip_header()` はプライベートだが、
243
+ gzip ヘッダーを扱う他のコードでも必要になりうる。
244
+ `nene2.io.gzip` モジュールとして昇格させる候補。
245
+ **Follow-up Issue 候補**: なし(現状の実装で十分)
246
+
247
+ ---
248
+
249
+ ## Follow-up Issues
250
+
251
+ | 優先度 | タイトル | 種別 |
252
+ |---|---|---|
253
+ | 低 | `GzipFile.name` が BytesIO で空になる挙動を How-to ドキュメントに記載 | docs |
254
+
255
+ ---
256
+
257
+ ## まとめ
258
+
259
+ FT181 では `gzip` モジュールを中心に、圧縮・解凍・メタデータ操作・ビルド再現性を実装した。
260
+ 40 テストが全通過し、mypy/ruff も問題なし。
261
+
262
+ 最大の発見は F-1: `GzipFile.name` が `fileobj=BytesIO` のとき常に空文字列を返す問題。
263
+ gzip ヘッダーのファイル名フィールドを取得するには、バイト列を直接解析する `_parse_gzip_header()` が必要。
264
+ この発見により、不正な gzip データの早期検出も正確になった。
265
+
266
+ `mtime=0.0` による決定論的圧縮は Docker イメージビルドや CI での
267
+ アーティファクトハッシュ検証に重要な知見。
268
+
269
+ v1.8.52 としてリリース。
@@ -214,6 +214,7 @@
214
214
  | [FT178](2026-05-field-trial-178.md) | base64 モジュール — エンコード・URL セーフ・データ URI | | |
215
215
  | [FT179](2026-05-field-trial-179.md) | zlib モジュール — 圧縮・解凍・展開爆弾対策・チェックサム | | |
216
216
  | [FT180](2026-05-field-trial-180.md) | xml モジュール — XXE/展開爆弾防御・構造検証・RSS パース | 🔒🔍 | [#506](https://github.com/hideyukiMORI/nene2-python/issues/506) [#507](https://github.com/hideyukiMORI/nene2-python/issues/507) |
217
+ | [FT181](2026-05-field-trial-181.md) | gzip モジュール — 圧縮・解凍・メタデータ・ビルド再現性 | | |
217
218
 
218
219
  ---
219
220
 
@@ -221,7 +222,7 @@
221
222
 
222
223
  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, 180
223
224
 
224
- 合計: **61件**(180 FT 中 約 34%)
225
+ 合計: **61件**(181 FT 中 約 34%)
225
226
 
226
227
  ## クラッカーペンテスト実施済み一覧(🔍)
227
228
 
@@ -229,4 +230,4 @@ FT172, FT176, FT180
229
230
 
230
231
  ---
231
232
 
232
- *最終更新: 2026-05-21 (FT180 / v1.8.51)*
233
+ *最終更新: 2026-05-21 (FT181 / v1.8.52)*
@@ -1,14 +1,14 @@
1
1
  # TODO — current
2
2
 
3
3
  最終更新: 2026-05-21
4
- 現状: **v1.8.51 安定版 / フィールドトライアルループ継続中(FT180 完了)**
4
+ 現状: **v1.8.52 安定版 / フィールドトライアルループ継続中(FT181 完了)**
5
5
 
6
6
  ---
7
7
 
8
8
  ## 状態サマリー
9
9
 
10
- v1.8.51 完了済み。FT180xml / XXE防御・展開爆弾対策・RSS パース)を含む FT180 件を実施済み。
11
- フィールドトライアルループは FT181 以降も継続中。
10
+ v1.8.52 完了済み。FT181gzip / 圧縮・解凍・メタデータ・ビルド再現性)を含む FT181 件を実施済み。
11
+ フィールドトライアルループは FT182 以降も継続中。
12
12
 
13
13
  ---
14
14
 
@@ -34,6 +34,7 @@ v1.8.51 完了済み。FT180(xml / XXE防御・展開爆弾対策・RSS パー
34
34
 
35
35
  | バージョン | 主な内容 |
36
36
  |---|---|
37
+ | v1.8.52 | FT181: gzip — 圧縮・解凍・メタデータ手動解析・ビルド再現性 |
37
38
  | v1.8.51 | FT180: xml — XXE/展開爆弾防御・RSS パース(診断+ペンテスト) |
38
39
  | v1.8.50 | FT179: zlib — 圧縮・解凍・展開爆弾対策・CRC32/Adler-32 |
39
40
  | v1.8.49 | FT178: base64 — エンコード・URL セーフ・データ URI・HTTP Basic Auth |
@@ -48,7 +49,7 @@ v1.8.51 完了済み。FT180(xml / XXE防御・展開爆弾対策・RSS パー
48
49
 
49
50
  ## フィールドトライアル進捗
50
51
 
51
- **実施済み**: FT1〜FT180(全 180 件)
52
+ **実施済み**: FT1〜FT181(全 181 件)
52
53
 
53
54
  索引: [`docs/field-trials/INDEX.md`](../field-trials/INDEX.md)
54
55
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nene2-python"
3
- version = "1.8.51"
3
+ version = "1.8.52"
4
4
  description = "NENE2 Python — minimal API framework following NENE2's design philosophy"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes