nene2-python 1.8.45__tar.gz → 1.8.46__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 (396) hide show
  1. {nene2_python-1.8.45 → nene2_python-1.8.46}/PKG-INFO +1 -1
  2. nene2_python-1.8.46/docs/field-trials/2026-05-field-trial-175.md +271 -0
  3. {nene2_python-1.8.45 → nene2_python-1.8.46}/pyproject.toml +1 -1
  4. {nene2_python-1.8.45 → nene2_python-1.8.46}/.env.example +0 -0
  5. {nene2_python-1.8.45 → nene2_python-1.8.46}/.github/workflows/ci.yml +0 -0
  6. {nene2_python-1.8.45 → nene2_python-1.8.46}/.github/workflows/docs.yml +0 -0
  7. {nene2_python-1.8.45 → nene2_python-1.8.46}/.github/workflows/publish.yml +0 -0
  8. {nene2_python-1.8.45 → nene2_python-1.8.46}/.gitignore +0 -0
  9. {nene2_python-1.8.45 → nene2_python-1.8.46}/.vitepress/config.mts +0 -0
  10. {nene2_python-1.8.45 → nene2_python-1.8.46}/.vitepress/theme/custom.css +0 -0
  11. {nene2_python-1.8.45 → nene2_python-1.8.46}/.vitepress/theme/index.ts +0 -0
  12. {nene2_python-1.8.45 → nene2_python-1.8.46}/AGENTS.md +0 -0
  13. {nene2_python-1.8.45 → nene2_python-1.8.46}/CHANGELOG.md +0 -0
  14. {nene2_python-1.8.45 → nene2_python-1.8.46}/CLAUDE.md +0 -0
  15. {nene2_python-1.8.45 → nene2_python-1.8.46}/Dockerfile +0 -0
  16. {nene2_python-1.8.45 → nene2_python-1.8.46}/LICENSE +0 -0
  17. {nene2_python-1.8.45 → nene2_python-1.8.46}/README.md +0 -0
  18. {nene2_python-1.8.45 → nene2_python-1.8.46}/alembic/README +0 -0
  19. {nene2_python-1.8.45 → nene2_python-1.8.46}/alembic/env.py +0 -0
  20. {nene2_python-1.8.45 → nene2_python-1.8.46}/alembic/script.py.mako +0 -0
  21. {nene2_python-1.8.45 → nene2_python-1.8.46}/alembic/versions/001_create_notes_and_tags_tables.py +0 -0
  22. {nene2_python-1.8.45 → nene2_python-1.8.46}/alembic.ini +0 -0
  23. {nene2_python-1.8.45 → nene2_python-1.8.46}/compose.yaml +0 -0
  24. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/adr/0001-toolchain.md +0 -0
  25. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/adr/0002-clean-architecture.md +0 -0
  26. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/adr/0003-security-first.md +0 -0
  27. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/adr/0004-ai-first-design.md +0 -0
  28. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/adr/0005-logging.md +0 -0
  29. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/adr/0006-rate-limiting.md +0 -0
  30. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/adr/0009-mcp-design.md +0 -0
  31. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/adr/0010-async-use-case.md +0 -0
  32. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/adr/0011-mcp-as-core-dependency.md +0 -0
  33. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/de/index.md +0 -0
  34. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/de/tutorials/getting-started.md +0 -0
  35. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/explanation/architecture.md +0 -0
  36. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/explanation/design-philosophy.md +0 -0
  37. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-1.md +0 -0
  38. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-10.md +0 -0
  39. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-100.md +0 -0
  40. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-101.md +0 -0
  41. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-102.md +0 -0
  42. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-103.md +0 -0
  43. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-104.md +0 -0
  44. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-105.md +0 -0
  45. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-106.md +0 -0
  46. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-107.md +0 -0
  47. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-108.md +0 -0
  48. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-109.md +0 -0
  49. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-11.md +0 -0
  50. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-110.md +0 -0
  51. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-111.md +0 -0
  52. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-112.md +0 -0
  53. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-113.md +0 -0
  54. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-114.md +0 -0
  55. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-115.md +0 -0
  56. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-116.md +0 -0
  57. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-117.md +0 -0
  58. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-118.md +0 -0
  59. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-119.md +0 -0
  60. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-12.md +0 -0
  61. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-120.md +0 -0
  62. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-121.md +0 -0
  63. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-122.md +0 -0
  64. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-123.md +0 -0
  65. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-124.md +0 -0
  66. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-125.md +0 -0
  67. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-126.md +0 -0
  68. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-127.md +0 -0
  69. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-128.md +0 -0
  70. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-129.md +0 -0
  71. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-13.md +0 -0
  72. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-130.md +0 -0
  73. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-131.md +0 -0
  74. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-132.md +0 -0
  75. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-133.md +0 -0
  76. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-134.md +0 -0
  77. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-135.md +0 -0
  78. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-136.md +0 -0
  79. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-137.md +0 -0
  80. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-138.md +0 -0
  81. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-139.md +0 -0
  82. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-14.md +0 -0
  83. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-140.md +0 -0
  84. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-141.md +0 -0
  85. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-142.md +0 -0
  86. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-143.md +0 -0
  87. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-144.md +0 -0
  88. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-145.md +0 -0
  89. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-146.md +0 -0
  90. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-147.md +0 -0
  91. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-148.md +0 -0
  92. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-149.md +0 -0
  93. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-15.md +0 -0
  94. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-150.md +0 -0
  95. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-151.md +0 -0
  96. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-152.md +0 -0
  97. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-153.md +0 -0
  98. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-154.md +0 -0
  99. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-155.md +0 -0
  100. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-156.md +0 -0
  101. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-157.md +0 -0
  102. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-158.md +0 -0
  103. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-159.md +0 -0
  104. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-16.md +0 -0
  105. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-160.md +0 -0
  106. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-161.md +0 -0
  107. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-162.md +0 -0
  108. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-163.md +0 -0
  109. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-164.md +0 -0
  110. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-165.md +0 -0
  111. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-166.md +0 -0
  112. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-167.md +0 -0
  113. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-168.md +0 -0
  114. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-169.md +0 -0
  115. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-17.md +0 -0
  116. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-170.md +0 -0
  117. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-171.md +0 -0
  118. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-172.md +0 -0
  119. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-173.md +0 -0
  120. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-174.md +0 -0
  121. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-18.md +0 -0
  122. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-19.md +0 -0
  123. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-2.md +0 -0
  124. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-20.md +0 -0
  125. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-21.md +0 -0
  126. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-22.md +0 -0
  127. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-23.md +0 -0
  128. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-24.md +0 -0
  129. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-25.md +0 -0
  130. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-26.md +0 -0
  131. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-27.md +0 -0
  132. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-28.md +0 -0
  133. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-29.md +0 -0
  134. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-3.md +0 -0
  135. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-30.md +0 -0
  136. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-31.md +0 -0
  137. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-32.md +0 -0
  138. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-33.md +0 -0
  139. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-34.md +0 -0
  140. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-35.md +0 -0
  141. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-36.md +0 -0
  142. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-37.md +0 -0
  143. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-38.md +0 -0
  144. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-39.md +0 -0
  145. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-4.md +0 -0
  146. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-40.md +0 -0
  147. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-41.md +0 -0
  148. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-42.md +0 -0
  149. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-43.md +0 -0
  150. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-44.md +0 -0
  151. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-45.md +0 -0
  152. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-46.md +0 -0
  153. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-47.md +0 -0
  154. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-48.md +0 -0
  155. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-49.md +0 -0
  156. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-5.md +0 -0
  157. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-50.md +0 -0
  158. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-51.md +0 -0
  159. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-52.md +0 -0
  160. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-53.md +0 -0
  161. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-54.md +0 -0
  162. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-55.md +0 -0
  163. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-56.md +0 -0
  164. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-57.md +0 -0
  165. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-58.md +0 -0
  166. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-59.md +0 -0
  167. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-6.md +0 -0
  168. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-60.md +0 -0
  169. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-61.md +0 -0
  170. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-62.md +0 -0
  171. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-63.md +0 -0
  172. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-64.md +0 -0
  173. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-65.md +0 -0
  174. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-66.md +0 -0
  175. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-67.md +0 -0
  176. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-68.md +0 -0
  177. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-69.md +0 -0
  178. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-7.md +0 -0
  179. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-70.md +0 -0
  180. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-71.md +0 -0
  181. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-72.md +0 -0
  182. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-73.md +0 -0
  183. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-74.md +0 -0
  184. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-75.md +0 -0
  185. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-76.md +0 -0
  186. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-77.md +0 -0
  187. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-78.md +0 -0
  188. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-79.md +0 -0
  189. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-8.md +0 -0
  190. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-80.md +0 -0
  191. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-81.md +0 -0
  192. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-82.md +0 -0
  193. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-83.md +0 -0
  194. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-84.md +0 -0
  195. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-85.md +0 -0
  196. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-86.md +0 -0
  197. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-87.md +0 -0
  198. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-88.md +0 -0
  199. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-89.md +0 -0
  200. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-9.md +0 -0
  201. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-90.md +0 -0
  202. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-91.md +0 -0
  203. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-92.md +0 -0
  204. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-93.md +0 -0
  205. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-94.md +0 -0
  206. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-95.md +0 -0
  207. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-96.md +0 -0
  208. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-97.md +0 -0
  209. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-98.md +0 -0
  210. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/field-trials/2026-05-field-trial-99.md +0 -0
  211. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/fr/index.md +0 -0
  212. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/fr/tutorials/getting-started.md +0 -0
  213. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/how-to/add-new-domain.md +0 -0
  214. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/how-to/api-versioning.md +0 -0
  215. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/how-to/async-use-case.md +0 -0
  216. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/how-to/background-tasks.md +0 -0
  217. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/how-to/configure-auth.md +0 -0
  218. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/how-to/cors.md +0 -0
  219. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/how-to/custom-auth-middleware.md +0 -0
  220. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/how-to/dependency-injection.md +0 -0
  221. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/how-to/domain-events.md +0 -0
  222. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/how-to/file-upload.md +0 -0
  223. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/how-to/lifespan-and-app-state.md +0 -0
  224. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/how-to/middleware-stack.md +0 -0
  225. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/how-to/new-project.md +0 -0
  226. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/how-to/problem-details.md +0 -0
  227. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/how-to/response-patterns.md +0 -0
  228. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/how-to/run-tests.md +0 -0
  229. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/how-to/soft-delete.md +0 -0
  230. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/how-to/sqlalchemy-repository.md +0 -0
  231. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/how-to/streaming.md +0 -0
  232. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/how-to/structured-logging.md +0 -0
  233. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/how-to/validation.md +0 -0
  234. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/how-to/webhook.md +0 -0
  235. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/howto/mcp-setup.md +0 -0
  236. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/index.md +0 -0
  237. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/ja/explanation/architecture.md +0 -0
  238. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/ja/explanation/design-philosophy.md +0 -0
  239. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/ja/how-to/add-new-domain.md +0 -0
  240. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/ja/how-to/configure-auth.md +0 -0
  241. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/ja/how-to/new-project.md +0 -0
  242. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/ja/how-to/run-tests.md +0 -0
  243. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/ja/how-to/sqlalchemy-repository.md +0 -0
  244. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/ja/howto/mcp-setup.md +0 -0
  245. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/ja/index.md +0 -0
  246. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/ja/reference/api.md +0 -0
  247. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/ja/reference/configuration.md +0 -0
  248. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/ja/reference/framework-modules.md +0 -0
  249. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/ja/tutorials/first-domain.md +0 -0
  250. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/ja/tutorials/getting-started.md +0 -0
  251. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/pt-br/index.md +0 -0
  252. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/pt-br/tutorials/getting-started.md +0 -0
  253. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/reference/api.md +0 -0
  254. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/reference/configuration.md +0 -0
  255. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/reference/framework-modules.md +0 -0
  256. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/roadmap.md +0 -0
  257. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/templates/field-trial-report.md +0 -0
  258. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/todo/current.md +0 -0
  259. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/tutorials/first-domain.md +0 -0
  260. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/tutorials/getting-started.md +0 -0
  261. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/zh/index.md +0 -0
  262. {nene2_python-1.8.45 → nene2_python-1.8.46}/docs/zh/tutorials/getting-started.md +0 -0
  263. {nene2_python-1.8.45 → nene2_python-1.8.46}/package-lock.json +0 -0
  264. {nene2_python-1.8.45 → nene2_python-1.8.46}/package.json +0 -0
  265. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/__init__.py +0 -0
  266. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/__main__.py +0 -0
  267. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/app.py +0 -0
  268. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/comment/__init__.py +0 -0
  269. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/comment/entity.py +0 -0
  270. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/comment/exceptions.py +0 -0
  271. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/comment/handler.py +0 -0
  272. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/comment/repository.py +0 -0
  273. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/comment/sqlalchemy_repository.py +0 -0
  274. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/comment/use_case.py +0 -0
  275. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/mcp.py +0 -0
  276. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/note/__init__.py +0 -0
  277. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/note/async_use_case.py +0 -0
  278. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/note/entity.py +0 -0
  279. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/note/exceptions.py +0 -0
  280. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/note/handler.py +0 -0
  281. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/note/repository.py +0 -0
  282. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/note/sqlalchemy_repository.py +0 -0
  283. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/note/use_case.py +0 -0
  284. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/schema.py +0 -0
  285. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/tag/__init__.py +0 -0
  286. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/tag/entity.py +0 -0
  287. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/tag/exceptions.py +0 -0
  288. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/tag/handler.py +0 -0
  289. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/tag/repository.py +0 -0
  290. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/tag/sqlalchemy_repository.py +0 -0
  291. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/example/tag/use_case.py +0 -0
  292. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/__init__.py +0 -0
  293. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/auth/__init__.py +0 -0
  294. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/auth/api_key.py +0 -0
  295. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/auth/bearer_token.py +0 -0
  296. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/auth/deps.py +0 -0
  297. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/auth/exceptions.py +0 -0
  298. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/auth/interfaces.py +0 -0
  299. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/auth/local_verifier.py +0 -0
  300. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/cache/__init__.py +0 -0
  301. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/cache/ttl.py +0 -0
  302. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/config/__init__.py +0 -0
  303. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/config/settings.py +0 -0
  304. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/database/__init__.py +0 -0
  305. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/database/exceptions.py +0 -0
  306. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/database/health.py +0 -0
  307. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/database/interfaces.py +0 -0
  308. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/database/sqlalchemy_executor.py +0 -0
  309. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/database/utils.py +0 -0
  310. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/http/__init__.py +0 -0
  311. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/http/etag.py +0 -0
  312. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/http/health.py +0 -0
  313. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/http/pagination.py +0 -0
  314. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/http/problem_details.py +0 -0
  315. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/log/__init__.py +0 -0
  316. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/log/setup.py +0 -0
  317. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/mcp/__init__.py +0 -0
  318. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/mcp/http_client.py +0 -0
  319. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/mcp/server.py +0 -0
  320. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/middleware/__init__.py +0 -0
  321. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/middleware/domain_exception.py +0 -0
  322. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/middleware/error_handler.py +0 -0
  323. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/middleware/request_id.py +0 -0
  324. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/middleware/request_logging.py +0 -0
  325. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/middleware/request_size_limit.py +0 -0
  326. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/middleware/security_headers.py +0 -0
  327. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/middleware/setup.py +0 -0
  328. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/middleware/throttle.py +0 -0
  329. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/py.typed +0 -0
  330. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/security/__init__.py +0 -0
  331. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/security/webhook.py +0 -0
  332. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/use_case/__init__.py +0 -0
  333. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/use_case/protocols.py +0 -0
  334. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/validation/__init__.py +0 -0
  335. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/nene2/validation/exceptions.py +0 -0
  336. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/scripts/__init__.py +0 -0
  337. {nene2_python-1.8.45 → nene2_python-1.8.46}/src/scripts/export_openapi.py +0 -0
  338. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/__init__.py +0 -0
  339. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/conftest.py +0 -0
  340. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/example/__init__.py +0 -0
  341. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/example/comment/__init__.py +0 -0
  342. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/example/comment/test_comment_http.py +0 -0
  343. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/example/comment/test_comment_repository.py +0 -0
  344. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/example/comment/test_comment_use_case.py +0 -0
  345. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/example/conftest.py +0 -0
  346. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/example/note/__init__.py +0 -0
  347. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/example/note/test_async_note_use_case.py +0 -0
  348. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/example/note/test_list_notes.py +0 -0
  349. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/example/note/test_note_repository.py +0 -0
  350. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/example/tag/__init__.py +0 -0
  351. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/example/tag/test_tag_repository.py +0 -0
  352. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/example/tag/test_tags.py +0 -0
  353. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/example/test_cors.py +0 -0
  354. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/example/test_mcp.py +0 -0
  355. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/__init__.py +0 -0
  356. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/auth/__init__.py +0 -0
  357. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/auth/test_api_key.py +0 -0
  358. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/auth/test_bearer_token.py +0 -0
  359. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/auth/test_make_require_auth.py +0 -0
  360. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/auth/test_token_issuer.py +0 -0
  361. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/cache/__init__.py +0 -0
  362. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/cache/test_ttl.py +0 -0
  363. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/config/__init__.py +0 -0
  364. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/config/test_settings.py +0 -0
  365. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/database/__init__.py +0 -0
  366. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/database/test_transaction.py +0 -0
  367. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/database/test_utils.py +0 -0
  368. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/http/__init__.py +0 -0
  369. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/http/test_etag.py +0 -0
  370. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/http/test_health.py +0 -0
  371. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/http/test_pagination.py +0 -0
  372. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/http/test_problem_details.py +0 -0
  373. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/log/__init__.py +0 -0
  374. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/log/test_setup.py +0 -0
  375. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/mcp/__init__.py +0 -0
  376. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/mcp/test_http_client.py +0 -0
  377. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/mcp/test_server.py +0 -0
  378. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/middleware/__init__.py +0 -0
  379. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/middleware/test_error_handler.py +0 -0
  380. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/middleware/test_request_id.py +0 -0
  381. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/middleware/test_request_logging.py +0 -0
  382. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/middleware/test_request_size_limit.py +0 -0
  383. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/middleware/test_security_headers.py +0 -0
  384. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/middleware/test_setup_middlewares.py +0 -0
  385. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/middleware/test_simple_domain_handler.py +0 -0
  386. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/middleware/test_throttle.py +0 -0
  387. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/security/__init__.py +0 -0
  388. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/security/test_webhook.py +0 -0
  389. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/use_case/__init__.py +0 -0
  390. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/use_case/test_protocols.py +0 -0
  391. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/use_case/test_run_in_threadpool.py +0 -0
  392. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/validation/__init__.py +0 -0
  393. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/nene2/validation/test_exceptions.py +0 -0
  394. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/scripts/__init__.py +0 -0
  395. {nene2_python-1.8.45 → nene2_python-1.8.46}/tests/scripts/test_export_openapi.py +0 -0
  396. {nene2_python-1.8.45 → nene2_python-1.8.46}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nene2-python
3
- Version: 1.8.45
3
+ Version: 1.8.46
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,271 @@
1
+ # FT175: logging モジュール
2
+
3
+ **日付**: 2026-05-21
4
+ **テーマ**: `logging` モジュール — センシティブデータマスキング・LoggerAdapter・dictConfig
5
+ **セキュリティ診断**: なし(FT176 で実施)
6
+
7
+ ---
8
+
9
+ ## 概要
10
+
11
+ Python 標準ライブラリの `logging` モジュールを検証する。
12
+ CLAUDE.md で「`print()` 禁止・`logging` モジュールのみ使用」と明示されており、
13
+ nene2-python の根幹ポリシーに直結するモジュールである。
14
+
15
+ このFTで確認する点:
16
+ - `logging.Filter` によるセンシティブデータのマスキング(パスワード・トークン・カード番号)
17
+ - `logging.LoggerAdapter` によるリクエストIDの全ログへの付与
18
+ - `setup_logger()` パターン(テスト用 StringIO へのキャプチャ)
19
+ - `parse_log_level()` によるログレベルの安全な変換
20
+ - `logging.config.dictConfig` による宣言的ログ設定
21
+ - `capture_logs()` / `release_capture()` テストユーティリティ
22
+
23
+ ---
24
+
25
+ ## 実装したサンプルアプリ
26
+
27
+ **場所**: `/home/xi/docker/nene2-python-FT/ft175-logging/`
28
+
29
+ ### 主要機能
30
+
31
+ | 関数/クラス | 概要 |
32
+ |---|---|
33
+ | `mask_sensitive(message)` | パスワード・トークン・API鍵・カード番号をマスク |
34
+ | `SensitiveFilter` | `logging.Filter` サブクラス — LogRecord のメッセージを自動マスク |
35
+ | `RequestIdAdapter` | `LoggerAdapter[Logger]` — 全ログに `[request_id]` を付与 |
36
+ | `setup_logger(name, level, stream)` | テスト用 StringIO 出力ロガーを作成 |
37
+ | `parse_log_level(level_str)` | 文字列 → `logging.DEBUG` 等の定数、不明は `INFO` |
38
+ | `log_event(logger, level, message, extra)` | 構造化ログエントリ(辞書)を記録して返す |
39
+ | `is_level_enabled(logger, level)` | ログレベルが有効かを `bool` で返す |
40
+ | `effective_level_name(logger)` | 有効ログレベル名を文字列で返す |
41
+ | `LOGGING_CONFIG` | `dictConfig` 用設定辞書(SensitiveFilter 組み込み) |
42
+ | `apply_logging_config()` | dictConfig を適用する |
43
+ | `capture_logs(logger)` | テスト用キャプチャハンドラーを追加して `(StringIO, handler)` を返す |
44
+ | `release_capture(logger, handler)` | キャプチャハンドラーを解放する |
45
+
46
+ マスキングパターン:
47
+
48
+ | パターン | 置換後 |
49
+ |---|---|
50
+ | `password=<4文字以上>` | `password=***` |
51
+ | `token: <4文字以上>` | `token: ***` |
52
+ | `api_key=<4文字以上>` | `api_key=***` |
53
+ | `\b\d{13,19}\b`(カード番号) | `****-****-****-****` |
54
+
55
+ ### HTTP エンドポイント
56
+
57
+ | メソッド | パス | 概要 |
58
+ |---|---|---|
59
+ | GET | `/logging/mask` | センシティブデータをマスクした文字列を返す |
60
+ | GET | `/logging/level` | ロガーのレベル有効判定 |
61
+ | POST | `/logging/event` | イベントを記録(センシティブマスク付き) |
62
+ | GET | `/logging/events` | 記録済みイベント一覧 |
63
+ | DELETE | `/logging/events` | イベント一覧をクリア |
64
+ | GET | `/logging/parse-level` | 文字列をログレベル数値に変換 |
65
+ | GET | `/logging/buffer` | インメモリバッファのログ行一覧 |
66
+
67
+ ---
68
+
69
+ ## テスト結果
70
+
71
+ **32 passed**
72
+
73
+ ```
74
+ 32 passed in 0.34s
75
+ ```
76
+
77
+ ---
78
+
79
+ ## 摩擦ポイント
80
+
81
+ ### F-1: `logging.StreamHandler` のジェネリック型注釈(深刻度: 低)
82
+
83
+ **事象**: `logging.StreamHandler` は `StreamHandler[StringIO]` とジェネリック型注釈できるが、
84
+ mypy が `# type: ignore[type-arg]` なしでは警告を出すケースがある。
85
+
86
+ **原因**: `StreamHandler` は Python 3.12 でジェネリック対応済みだが、
87
+ `StreamHandler` の型スタブが `Generic[TextIO]` として定義されているため
88
+ `StreamHandler` 単独だと型引数省略警告が出ることがある。
89
+
90
+ **対応**: 関数シグネチャに `# type: ignore[type-arg]` を追記。
91
+ これは mypy の型スタブ側の制約であり実装ミスではないため、CLAUDE.md 規約に従いコード添付コメントで理由を明記した。
92
+
93
+ ### F-2: `LoggerAdapter.extra` の型(深刻度: 低)
94
+
95
+ **事象**: `LoggerAdapter[Logger]` の `self.extra` の型が `Mapping[str, Any] | None` なので
96
+ `self.extra.get("request_id")` の前に `if self.extra` のガードが必要。
97
+
98
+ **原因**: `LoggerAdapter.__init__` の `extra` パラメーターが `Mapping[str, Any] | None` で
99
+ 初期化できるため、mypy がnullチェックを要求する。
100
+
101
+ **対応**: `request_id = self.extra.get("request_id", "-") if self.extra else "-"` で対応。
102
+
103
+ ---
104
+
105
+ ## 観察点
106
+
107
+ ### 観察1: `SensitiveFilter` によるパイプライン的マスキング
108
+
109
+ ```python
110
+ class SensitiveFilter(logging.Filter):
111
+ def filter(self, record: logging.LogRecord) -> bool:
112
+ record.msg = mask_sensitive(str(record.msg))
113
+ if record.args:
114
+ if isinstance(record.args, dict):
115
+ record.args = {k: mask_sensitive(str(v)) for k, v in record.args.items()}
116
+ elif isinstance(record.args, tuple):
117
+ record.args = tuple(mask_sensitive(str(a)) for a in record.args)
118
+ return True
119
+ ```
120
+
121
+ `logging.Logger.info("user %s password=%s", username, password)` のように
122
+ フォーマット引数を分離して渡すケースでは `record.args` をマスクする必要がある。
123
+ `record.msg` だけマスクしても `%s` 置換後の最終文字列に平文が現れるためである。
124
+
125
+ ### 観察2: `RequestIdAdapter` でコンテキストを注入する
126
+
127
+ ```python
128
+ class RequestIdAdapter(logging.LoggerAdapter[logging.Logger]):
129
+ def process(self, msg: str, kwargs: Any) -> tuple[str, Any]:
130
+ request_id = self.extra.get("request_id", "-") if self.extra else "-"
131
+ return f"[{request_id}] {msg}", kwargs
132
+ ```
133
+
134
+ FastAPI のリクエストスコープで `RequestIdAdapter` インスタンスを生成し、
135
+ 同一リクエスト内の全ログに `[req-xxxxxxxx]` プレフィックスを付与できる。
136
+ `logging.getLogger()` グローバルシングルトンと異なり、スコープごとに別インスタンスを作れる。
137
+
138
+ ### 観察3: `parse_log_level` の安全な変換
139
+
140
+ ```python
141
+ def parse_log_level(level_str: str) -> int:
142
+ level = logging.getLevelName(level_str.upper())
143
+ if not isinstance(level, int): # 不明な文字列は int ではなく文字列を返す
144
+ return logging.INFO
145
+ return level
146
+ ```
147
+
148
+ `logging.getLevelName("UNKNOWN")` は `"Level UNKNOWN"` という文字列を返す(None ではない)。
149
+ `isinstance(level, int)` チェックでフォールバックを実装する必要がある。
150
+
151
+ ### 観察4: `dictConfig` で `SensitiveFilter` を `()` 形式でインスタンス化
152
+
153
+ ```python
154
+ "filters": {
155
+ "sensitive": {
156
+ "()": SensitiveFilter, # クラスを直接参照してインスタンス化
157
+ },
158
+ },
159
+ ```
160
+
161
+ `logging.config.dictConfig` では `"()"` キーを使ってカスタムクラスのコンストラクターを呼べる。
162
+ `class` キーは `logging` 組み込みクラスのみに使われ、カスタムクラスには `()` を使う。
163
+
164
+ ---
165
+
166
+ ## nene2-python フレームワークとの統合
167
+
168
+ - nene2-python の `nene2.log` には `structlog` ベースの設定が既に存在する。
169
+ `logging` モジュールの `SensitiveFilter` パターンは `structlog` の `processor` と
170
+ 役割が対応する(`structlog` では `ProcessorFormatter` で同様のマスキングが可能)
171
+ - CLAUDE.md の「`logging` モジュールのみ使用(`print()` 禁止)」を実証する FT となった
172
+ - `RequestIdAdapter` と nene2 の `RequestIdMiddleware` が生成する `x-request-id` を
173
+ 連動させることで、ログとHTTPレスポンスの追跡IDを統一できる
174
+
175
+ ---
176
+
177
+ ## Developer Experience (DX) Review
178
+
179
+ ### ペルソナ1: 初心者(Python 歴1年・独学中・女性・バックエンド志望)
180
+
181
+ `logging.basicConfig(level=logging.DEBUG)` から入る人には、
182
+ `logging.Filter` のサブクラス化は最初のハードルになる。
183
+
184
+ **ドキュメント理解**: `filter()` が `True` を返すとログ通過、`False` で破棄という
185
+ Python の慣習は直感と逆に感じる場合がある(True = 「フィルターを通す」という意味)。
186
+ **事故リスク**: 高。`record.args` のマスクを忘れてフォーマット引数に平文パスワードが
187
+ 残るパターンは気づきにくい。`SensitiveFilter` のような共通フィルターを強制するのが安全。
188
+ **規約の使いやすさ**: `setup_logger()` ファクトリ関数でテスト・本番の切り替えができる。
189
+
190
+ ### ペルソナ2: ロースキル経験者(Python 歴3-4年・スクリプト系・男性・SES)
191
+
192
+ `print()` から `logging` への移行は「なぜ面倒なことをするのか」と感じやすい層。
193
+ センシティブデータの事故事例を見せることが動機付けに有効。
194
+
195
+ **コピペ可能性**: `setup_logger()` はそのままコピーして使える。
196
+ **拡張時の罠**: `logger.propagate = False` を忘れると親ロガーにも出力され、
197
+ 二重ログ・センシティブデータ漏洩につながる。
198
+ **セキュリティ的な事故リスク**: 高。パスワードをログに書いて CloudWatch / Datadog に流れた
199
+ インシデントは実際に多数報告されている。`SensitiveFilter` は必須。
200
+
201
+ ### ペルソナ3: フロントエンド寄り経験者(React/TS 歴4年・バックエンド転向中・ノンバイナリ)
202
+
203
+ Node.js の `winston` / `pino` と比較すると Python の `logging` は低レベル API に感じるが、
204
+ `dictConfig` による宣言的設定で同様の構成が可能。
205
+
206
+ **エラーレスポンスの質**: `/logging/mask` エンドポイントで
207
+ 「original_length(入力長)+ masked(マスク後)」を返す設計でクライアント側が動作確認しやすい。
208
+ **Python 固有概念の学習コスト**: `LogRecord` の `msg` と `args` の分離は
209
+ Python %-style フォーマットの知識が必要で、JS 開発者には説明が必要。
210
+ **事故リスク**: 中。HTTP API として使う分には Pydantic が入力を保護している。
211
+
212
+ ### ペルソナ4: バックエンド経験者(Django/FastAPI 歴5-6年・男性・リードエンジニア)
213
+
214
+ Django の `LOGGING` 設定辞書と `dictConfig` はほぼ同じ形式なので即理解できる。
215
+ `SensitiveFilter` の `record.args` マスクは見落としがちなポイントで評価が高い。
216
+
217
+ **他フレームワークとの差異**: Django の `LOGGING` は `dictConfig` のラッパー。
218
+ FastAPI では自前で `logging.config.dictConfig()` を `lifespan` で呼ぶ必要がある。
219
+ **nene2-python の薄さへの評価**: `structlog` との共存方法を明確にするドキュメントがほしい。
220
+ **本番投入可能性**: `SensitiveFilter` + `RequestIdAdapter` のペアは本番環境でそのまま使える品質。
221
+
222
+ ### ペルソナ5: シニアエンジニア(設計・コードレビュー担当・女性・10-12年)
223
+
224
+ `record.args` のマスク実装が適切。ただし `%` フォーマット以外(`{}`形式)への対応が未検討。
225
+
226
+ **コードレビューチェックポイント**:
227
+ - [x] `SensitiveFilter.filter()` が `record.args` も処理しているか — OK
228
+ - [x] `mask_sensitive()` の正規表現が ReDoS リスクを持っていないか — `[^\s,\"'&;]{4,}` は量指定子が単純で爆発しない ✅
229
+ - [ ] `logging.LogRecord.args` が `dict` / `tuple` 以外の型(例: `int`)のとき `isinstance` チェックが抜ける — `else` ブロックなし(稀なケースで問題発生する可能性)
230
+ - [ ] `_LOGGED_EVENTS: list[dict]` がモジュールレベルのグローバル変数 — 複数 `create_app()` 呼び出し時に状態が混在する
231
+
232
+ **チームでの安全な共有パターン**: `SensitiveFilter` を `nene2.log` に組み込み、
233
+ デフォルトで全ロガーに適用される設計が理想。
234
+ **ツール追加の必要性**: `ruff S314` (print statement) は `print()` を禁止するが、
235
+ Python 3 では `print` は関数なので別アプローチ(プリコミットフック等)が必要。
236
+
237
+ ### ペルソナ6: 設計者・ポリシー照合(nene2-python 設計ポリシー目線)
238
+
239
+ CLAUDE.md の「`logging` モジュールのみ使用」ポリシーを、実際の使用パターンとして実証した。
240
+
241
+ **ポリシー達成度**: 高
242
+ **「初心者でも安全な API」達成度**: 中(`record.args` マスク忘れのリスクがある)
243
+ **設計上の負債・ドキュメント不足**:
244
+ - `nene2.log` の `structlog` 設定と標準 `logging` の共存について ADR が必要
245
+ - `SensitiveFilter` を `nene2-python` フレームワーク本体に組み込む価値がある
246
+ - `LOGGING_CONFIG` 辞書は `nene2.log` の設定と統一すべき
247
+
248
+ **Follow-up Issue 候補**: `SensitiveFilter` を `nene2.middleware` または `nene2.log` に追加
249
+
250
+ ---
251
+
252
+ ## Follow-up Issues
253
+
254
+ | 優先度 | タイトル | 種別 |
255
+ |---|---|---|
256
+ | 中 | `SensitiveFilter` を `nene2.log` または `nene2.middleware` に追加 | feat |
257
+ | 中 | `structlog` と標準 `logging` の共存 ADR を作成 | docs |
258
+ | 低 | `SensitiveFilter.filter()` の `record.args` が dict/tuple 以外のとき `str` として処理する | fix |
259
+
260
+ ---
261
+
262
+ ## まとめ
263
+
264
+ FT175 では `logging` モジュールの実践的パターンを実証した。
265
+ `SensitiveFilter` による `record.args` を含む完全マスキング、
266
+ `RequestIdAdapter` によるリクエストスコープのコンテキスト注入、
267
+ `dictConfig` による宣言的設定が核心。
268
+ `record.args` のマスク忘れは実際の本番事故につながるため、
269
+ `SensitiveFilter` を nene2-python フレームワーク本体に取り込むことを検討すべき。
270
+
271
+ 次の FT176 は 176 % 3 = 2 → セキュリティ診断なし。176 % 4 = 0 → クラッカーペンテスト実施。
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nene2-python"
3
- version = "1.8.45"
3
+ version = "1.8.46"
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