agentic-dev-loop 1.0.0b1__tar.gz → 1.0.0rc2__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 (219) hide show
  1. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/PKG-INFO +2 -2
  2. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/README.md +1 -1
  3. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/pyproject.toml +1 -1
  4. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/hostboundary.py +2 -1
  5. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/onboarding/upgrade.py +5 -1
  6. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/run_status_command.md +1 -1
  7. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop.egg-info/PKG-INFO +2 -2
  8. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop.egg-info/SOURCES.txt +1 -0
  9. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_055_upgrade.py +19 -1
  10. agentic_dev_loop-1.0.0rc2/tests/test_pypi_yank.py +339 -0
  11. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/LICENSE +0 -0
  12. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/setup.cfg +0 -0
  13. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/__init__.py +0 -0
  14. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/core/__init__.py +0 -0
  15. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/core/config.py +0 -0
  16. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/core/failure.py +0 -0
  17. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/core/paths.py +0 -0
  18. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/core/profiles/__init__.py +0 -0
  19. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/core/profiles/data/dotnet-node.yaml +0 -0
  20. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/core/profiles/data/python.yaml +0 -0
  21. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/core/results.py +0 -0
  22. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/core/verdict.py +0 -0
  23. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/__init__.py +0 -0
  24. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/actions.py +0 -0
  25. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/agent_error_guide.md +0 -0
  26. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/agent_error_guide.py +0 -0
  27. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/agents.py +0 -0
  28. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/allowlist.py +0 -0
  29. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/brief_coverage.py +0 -0
  30. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/brief_prompt.md +0 -0
  31. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/brief_prompt.py +0 -0
  32. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/cli.py +0 -0
  33. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/completion.py +0 -0
  34. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/containers.py +0 -0
  35. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/copilot_auth.py +0 -0
  36. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/deps.py +0 -0
  37. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/dryrun.py +0 -0
  38. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/egress_policy.py +0 -0
  39. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/egress_proxy.py +0 -0
  40. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/extensions/__init__.py +0 -0
  41. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/extensions/aidlc.py +0 -0
  42. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/forbidden.py +0 -0
  43. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/graph.py +0 -0
  44. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/isolation.py +0 -0
  45. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/llm.py +0 -0
  46. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/loaders.py +0 -0
  47. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/mounts.py +0 -0
  48. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/onboarding/__init__.py +0 -0
  49. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/onboarding/checks.py +0 -0
  50. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/onboarding/doctor.py +0 -0
  51. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/onboarding/init.py +0 -0
  52. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/onboarding/recipes/dotnet-node.yaml +0 -0
  53. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/onboarding/recipes/python.yaml +0 -0
  54. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/onboarding/recipes.py +0 -0
  55. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/onboarding/templates/dev.agent.md +0 -0
  56. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/onboarding/templates/env.example +0 -0
  57. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/onboarding/templates/qa.agent.md +0 -0
  58. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/onboarding/templates/review.agent.md +0 -0
  59. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/onboarding/templates.py +0 -0
  60. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/outputs.py +0 -0
  61. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/presets.py +0 -0
  62. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/proc.py +0 -0
  63. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/provider_wire.py +0 -0
  64. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/registry.py +0 -0
  65. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/run_status.py +0 -0
  66. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/seccomp/__init__.py +0 -0
  67. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/seccomp/chromium-userns.json +0 -0
  68. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/secrets.py +0 -0
  69. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/stages.py +0 -0
  70. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/step_workspace.py +0 -0
  71. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/engine/sync_back.py +0 -0
  72. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/py.typed +0 -0
  73. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/reporting/__init__.py +0 -0
  74. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/reporting/metrics.py +0 -0
  75. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/reporting/render.py +0 -0
  76. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/reporting/sources.py +0 -0
  77. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/__init__.py +0 -0
  78. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/layout.py +0 -0
  79. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/page.py +0 -0
  80. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/runs_view.py +0 -0
  81. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/server.py +0 -0
  82. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/16-1pKPtbcx.js +0 -0
  83. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/16-2lMgbefg.js +0 -0
  84. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/16-B0WgRfVg.js +0 -0
  85. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/16-B3fCnGwv.js +0 -0
  86. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/16-Bm3KyEZC.js +0 -0
  87. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/16-DIfh6y3Q.js +0 -0
  88. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/16-DxEg5YJZ.js +0 -0
  89. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/20-CdViZ4xF.js +0 -0
  90. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/accordion.min.js +0 -0
  91. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/button-rSg_K8yf.js +0 -0
  92. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/button-skeleton-i-vqINhS.js +0 -0
  93. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/button.min.js +0 -0
  94. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/carbon-element-Bi7pQHAl.js +0 -0
  95. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/checkbox-D7PSLQCq.js +0 -0
  96. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/checkbox.min.js +0 -0
  97. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/class-map-t9xXbTFr.js +0 -0
  98. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/code-snippet.min.js +0 -0
  99. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/collection-helpers-BOkEorAG.js +0 -0
  100. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/copy-button.min.js +0 -0
  101. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/copy-tonlUGaV.js +0 -0
  102. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/data-table.min.js +0 -0
  103. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/decorate-1Ivfrp93.js +0 -0
  104. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/decorators-CNcdZSLL.js +0 -0
  105. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/deep-shadow-contains-Cs2rSnhr.js +0 -0
  106. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/definition-tooltip-FYe4J71p.js +0 -0
  107. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/directive-xDpGnViN.js +0 -0
  108. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/feature-flags-C7ZDYm5Q.js +0 -0
  109. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/floating-controller-DmTRVV0A.js +0 -0
  110. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/focus-D91D_iCB.js +0 -0
  111. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/form-Sg67yyAA.js +0 -0
  112. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/host-listener-CEd01uiB.js +0 -0
  113. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/host-listener-D_ODAJ3e.js +0 -0
  114. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/icon-button-Be2x1qrf.js +0 -0
  115. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/icon-loader-Cj1PggRN.js +0 -0
  116. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/icon-loader-utils-CVerE28u.js +0 -0
  117. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/if-defined-CV6i5LeB.js +0 -0
  118. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/if-non-empty-B5mz8bH9.js +0 -0
  119. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/link-aD3OYHCu.js +0 -0
  120. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/link.min.js +0 -0
  121. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/manifest.json +0 -0
  122. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/notification.min.js +0 -0
  123. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/on-DP_Dnlgd.js +0 -0
  124. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/popover-content-fajgXUvu.js +0 -0
  125. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/popover.min.js +0 -0
  126. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/query-DJR3NpB4.js +0 -0
  127. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/query-all-BuL-eXl_.js +0 -0
  128. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/radio-button.min.js +0 -0
  129. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/radio-group-manager-BE85t4HN.js +0 -0
  130. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/search-CVX2VAop.js +0 -0
  131. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/settings-DlHDxgf_.js +0 -0
  132. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/skeleton-text-s5gA9_CO.js +0 -0
  133. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/skeleton-text.min.js +0 -0
  134. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/tag.min.js +0 -0
  135. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/themes.css +0 -0
  136. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/tile.min.js +0 -0
  137. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/tooltip-DYtXwn-o.js +0 -0
  138. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/tooltip-content-DDo9ICPL.js +0 -0
  139. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/tooltip.min.js +0 -0
  140. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/ui-shell.min.js +0 -0
  141. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop/viz/vendor/unsafe-html-BDr7jYB8.js +0 -0
  142. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop.egg-info/dependency_links.txt +0 -0
  143. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop.egg-info/entry_points.txt +0 -0
  144. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop.egg-info/requires.txt +0 -0
  145. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/src/agentic_dev_loop.egg-info/top_level.txt +0 -0
  146. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_029_merge_on_conflict.py +0 -0
  147. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_033_brief_step.py +0 -0
  148. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_035_finalize_nudge.py +0 -0
  149. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_036_banned_commands_and_lane_boundary.py +0 -0
  150. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_041_brief_coverage.py +0 -0
  151. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_046_brief_prompt.py +0 -0
  152. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_055_cli.py +0 -0
  153. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_055_doctor.py +0 -0
  154. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_055_init.py +0 -0
  155. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_055_profile_loading.py +0 -0
  156. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_055_profiles.py +0 -0
  157. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_055_recipes.py +0 -0
  158. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_055_schema_lock.py +0 -0
  159. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_055_templates.py +0 -0
  160. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_actions.py +0 -0
  161. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_agent_error_guide.py +0 -0
  162. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_agents.py +0 -0
  163. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_aidlc.py +0 -0
  164. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_build_viz_page.py +0 -0
  165. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_cli.py +0 -0
  166. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_config.py +0 -0
  167. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_containers.py +0 -0
  168. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_copilot_auth.py +0 -0
  169. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_copilot_cli.py +0 -0
  170. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_deps.py +0 -0
  171. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_dry_run.py +0 -0
  172. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_e2e_fixtures.py +0 -0
  173. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_egress_proxy.py +0 -0
  174. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_graph.py +0 -0
  175. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_hostboundary.py +0 -0
  176. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_hostboundary_integration.py +0 -0
  177. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_isolation.py +0 -0
  178. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_isolation_integration.py +0 -0
  179. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_isolation_pbt.py +0 -0
  180. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_llm.py +0 -0
  181. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_loaders.py +0 -0
  182. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_module_contract.py +0 -0
  183. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_outputs.py +0 -0
  184. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_outputs_pbt.py +0 -0
  185. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_proc.py +0 -0
  186. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_provider_wire.py +0 -0
  187. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_publish.py +0 -0
  188. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_qa_prompt.py +0 -0
  189. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_registry.py +0 -0
  190. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_repo_ascii.py +0 -0
  191. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_results.py +0 -0
  192. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_run_status.py +0 -0
  193. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_runs_js_duration_charts.py +0 -0
  194. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_runs_js_filters.py +0 -0
  195. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_runs_js_key_stats.py +0 -0
  196. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_runs_js_metrics.py +0 -0
  197. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_runs_layout.py +0 -0
  198. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_runs_layout_key_stats.py +0 -0
  199. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_sdk_compat.py +0 -0
  200. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_secrets.py +0 -0
  201. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_secrets_integration.py +0 -0
  202. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_secrets_pipeline.py +0 -0
  203. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_source_resolution.py +0 -0
  204. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_stages.py +0 -0
  205. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_step_workspace.py +0 -0
  206. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_sync_back.py +0 -0
  207. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_unit11.py +0 -0
  208. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_unit11_pbt.py +0 -0
  209. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_unit12_reporting.py +0 -0
  210. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_unit13_runs_viz.py +0 -0
  211. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_unit5_config.py +0 -0
  212. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_unit5_pbt.py +0 -0
  213. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_unit5_pure.py +0 -0
  214. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_vendor_carbon_wc.py +0 -0
  215. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_verdict.py +0 -0
  216. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_viz.py +0 -0
  217. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_viz_demo.py +0 -0
  218. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_viz_layout.py +0 -0
  219. {agentic_dev_loop-1.0.0b1 → agentic_dev_loop-1.0.0rc2}/tests/test_viz_wheel_contents.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentic-dev-loop
3
- Version: 1.0.0b1
3
+ Version: 1.0.0rc2
4
4
  Summary: Turn an LLM into a gated, sandboxed, resumable construction loop over your codebase.
5
5
  Author: Luke Wilson
6
6
  License-Expression: Apache-2.0
@@ -113,7 +113,7 @@ Docker engine does:
113
113
  ### Installation
114
114
 
115
115
  ```bash
116
- pipx install agentic-dev-loop
116
+ pipx install --pip-args="--pre" agentic-dev-loop # latest release is currently a pre-release
117
117
  agentic-dev-loop --version
118
118
  ```
119
119
 
@@ -62,7 +62,7 @@ Docker engine does:
62
62
  ### Installation
63
63
 
64
64
  ```bash
65
- pipx install agentic-dev-loop
65
+ pipx install --pip-args="--pre" agentic-dev-loop # latest release is currently a pre-release
66
66
  agentic-dev-loop --version
67
67
  ```
68
68
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "agentic-dev-loop"
7
- version = "1.0.0b1"
7
+ version = "1.0.0rc2"
8
8
  description = "Turn an LLM into a gated, sandboxed, resumable construction loop over your codebase."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -58,7 +58,8 @@ def check_orchestrator_install(workspace: str | Path,
58
58
  raise HostBoundaryError(
59
59
  f"agentic_dev_loop resolves to {resolved}, inside the workspace "
60
60
  f"{ws} - the engine must be installed outside the workspace "
61
- "(e.g. `pipx install agentic-dev-loop`); refusing to run")
61
+ "(e.g. `pipx install --pip-args=--pre agentic-dev-loop`); "
62
+ "refusing to run")
62
63
 
63
64
 
64
65
  def tooling_rel_paths(cfg: "PipelineConfig", pipeline_path: str | Path,
@@ -43,8 +43,12 @@ def run_upgrade(argv: list[str], *, workspace: Path | None = None,
43
43
  "pipx-managed", file=sys.stderr)
44
44
  return 1
45
45
 
46
+ # --pre is required: the release process yanks superseded releases, and
47
+ # the current latest (1.0.0b1) is a pre-release, so a plain install has
48
+ # no satisfiable version. pipx translates --pre on both backends (pip and
49
+ # uv); dropping it here breaks upgrade until a stable release ships.
46
50
  result = subprocess.run(
47
- [pipx, "install", "--force", "agentic-dev-loop"],
51
+ [pipx, "install", "--force", "--pip-args=--pre", "agentic-dev-loop"],
48
52
  capture_output=True, text=True, check=False, env=env)
49
53
  if result.returncode != 0:
50
54
  detail = (result.stderr or "unknown error").strip()
@@ -20,4 +20,4 @@ The command prints two sections: `===MERMAID===` then `===SUMMARY===`.
20
20
 
21
21
  ## Errors
22
22
 
23
- - If `agentic-dev-loop` is not on PATH, report that the engine is not installed (`pipx install agentic-dev-loop`) and stop.
23
+ - If `agentic-dev-loop` is not on PATH, report that the engine is not installed (`pipx install --pip-args="--pre" agentic-dev-loop`) and stop.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentic-dev-loop
3
- Version: 1.0.0b1
3
+ Version: 1.0.0rc2
4
4
  Summary: Turn an LLM into a gated, sandboxed, resumable construction loop over your codebase.
5
5
  Author: Luke Wilson
6
6
  License-Expression: Apache-2.0
@@ -113,7 +113,7 @@ Docker engine does:
113
113
  ### Installation
114
114
 
115
115
  ```bash
116
- pipx install agentic-dev-loop
116
+ pipx install --pip-args="--pre" agentic-dev-loop # latest release is currently a pre-release
117
117
  agentic-dev-loop --version
118
118
  ```
119
119
 
@@ -182,6 +182,7 @@ tests/test_outputs_pbt.py
182
182
  tests/test_proc.py
183
183
  tests/test_provider_wire.py
184
184
  tests/test_publish.py
185
+ tests/test_pypi_yank.py
185
186
  tests/test_qa_prompt.py
186
187
  tests/test_registry.py
187
188
  tests/test_repo_ascii.py
@@ -49,12 +49,30 @@ def test_upgrade_installs_latest_and_writes_pin(tmp_path, capsys):
49
49
  code = run_upgrade([], workspace=tmp_path, env=_env_with(bin_dir))
50
50
 
51
51
  assert code == 0
52
- assert pipx_log.read_text().strip() == "install --force agentic-dev-loop"
52
+ assert pipx_log.read_text().strip() == \
53
+ "install --force --pip-args=--pre agentic-dev-loop"
53
54
  pin = tmp_path / "agentic-dev-loop" / ".engine-version"
54
55
  assert pin.read_text(encoding="utf-8") == "0.10.4\n"
55
56
  assert "upgraded agentic-dev-loop to 0.10.4" in capsys.readouterr().out
56
57
 
57
58
 
59
+ def test_upgrade_pins_pre_release_version(tmp_path):
60
+ # The PyPI index currently ships 1.0.0b1 as the only non-yanked release,
61
+ # so the pin step must accept pre-release versions (regression: upgrade
62
+ # failed because pipx never saw 1.0.0b1 as installable).
63
+ bin_dir = _stub_env(
64
+ tmp_path,
65
+ pipx_body="exit 0",
66
+ cli_body='echo "agentic-dev-loop 1.0.0b1"',
67
+ )
68
+
69
+ code = run_upgrade([], workspace=tmp_path, env=_env_with(bin_dir))
70
+
71
+ assert code == 0
72
+ pin = tmp_path / "agentic-dev-loop" / ".engine-version"
73
+ assert pin.read_text(encoding="utf-8") == "1.0.0b1\n"
74
+
75
+
58
76
  def test_upgrade_overwrites_existing_pin(tmp_path):
59
77
  out = tmp_path / "agentic-dev-loop"
60
78
  out.mkdir()
@@ -0,0 +1,339 @@
1
+ """Tests for tools/pypi_yank.py - the PyPI release yank script.
2
+
3
+ PyPI has no public API for yanking releases, so the tool drives the same
4
+ HTML forms the web UI submits. The HTTP session is thin glue; everything
5
+ decision-shaped (which versions to yank, which form to fill, whether a
6
+ re-auth is being demanded, whether the release is already yanked) lives in
7
+ pure functions tested here.
8
+
9
+ The release fixture is a trimmed dump of the real PyPI JSON API response
10
+ for agentic-dev-loop, fetched 2026-11-11 (filenames and yanked flags are
11
+ the real ones). The HTML fixtures mirror the rendered warehouse manage
12
+ pages (yank form, un-yank form, re-auth form, two-factor form) field for
13
+ field, per warehouse/manage/views/__init__.py (POST triggered on
14
+ confirm_yank_version, require_reauth) and
15
+ warehouse/templates/manage/project/release.html.
16
+ """
17
+
18
+ from pathlib import Path
19
+
20
+ import pytest
21
+ from packaging.version import Version
22
+
23
+ REPO_ROOT = Path(__file__).resolve().parent.parent
24
+ YANK = REPO_ROOT / "tools" / "pypi_yank.py"
25
+
26
+ import importlib.util # noqa: E402
27
+
28
+
29
+ def _load_yank():
30
+ spec = importlib.util.spec_from_file_location("pypi_yank", YANK)
31
+ module = importlib.util.module_from_spec(spec)
32
+ spec.loader.exec_module(module)
33
+ return module
34
+
35
+
36
+ yank = _load_yank()
37
+
38
+ # Trimmed real dump: https://pypi.org/pypi/agentic-dev-loop/json
39
+ # (2026-11-11). Every 0.x release carries a wheel and an sdist, all
40
+ # unyanked. 1.0.0a1 (the keep target) had not been published yet at dump
41
+ # time, which the absent-keep tests exercise.
42
+ REAL_RELEASES = {
43
+ "0.3.0": [
44
+ {"filename": "agentic_dev_loop-0.3.0-py3-none-any.whl",
45
+ "yanked": False},
46
+ {"filename": "agentic_dev_loop-0.3.0.tar.gz", "yanked": False},
47
+ ],
48
+ "0.10.0": [
49
+ {"filename": "agentic_dev_loop-0.10.0-py3-none-any.whl",
50
+ "yanked": False},
51
+ {"filename": "agentic_dev_loop-0.10.0.tar.gz", "yanked": False},
52
+ ],
53
+ "0.15.7": [
54
+ {"filename": "agentic_dev_loop-0.15.7-py3-none-any.whl",
55
+ "yanked": False},
56
+ {"filename": "agentic_dev_loop-0.15.7.tar.gz", "yanked": False},
57
+ ],
58
+ }
59
+
60
+ # Warehouse manage release page, unyanked release: the Yank release form
61
+ # (confirm_yank_version text input, optional yanked_reason, csrf) plus a
62
+ # per-file delete form, which must NOT be mistaken for the yank form.
63
+ YANK_PAGE = """<!DOCTYPE html>
64
+ <html><body>
65
+ <form method="POST" action="/manage/project/agentic-dev-loop/release/0.15.7/">
66
+ <input name="csrf_token" type="hidden" value="TOKEN123">
67
+ <input name="confirm_yank_version" type="text" value="">
68
+ <input name="yanked_reason" type="text" value="">
69
+ <button type="submit">Yank release</button>
70
+ </form>
71
+ <form method="POST" action="/manage/project/agentic-dev-loop/release/0.15.7/file/agentic_dev_loop-0.15.7.tar.gz/">
72
+ <input name="csrf_token" type="hidden" value="TOKEN777">
73
+ <input name="confirm_delete_version" type="text" value="">
74
+ <button type="submit">Delete file</button>
75
+ </form>
76
+ </body></html>"""
77
+
78
+ # Warehouse manage release page, yanked release: the Un-yank form replaces
79
+ # the yank form. This is both the post-yank verification signal and the
80
+ # idempotence signal on a re-run.
81
+ UNYANK_PAGE = """<!DOCTYPE html>
82
+ <html><body>
83
+ <form method="POST" action="/manage/project/agentic-dev-loop/release/0.15.7/">
84
+ <input name="csrf_token" type="hidden" value="TOKEN123">
85
+ <input name="confirm_unyank_version" type="text" value="">
86
+ <button type="submit">Un-yank release</button>
87
+ </form>
88
+ </body></html>"""
89
+
90
+ # Warehouse login page, for the session-expired guard in verification.
91
+ LOGIN_PAGE = """<!DOCTYPE html>
92
+ <html><body>
93
+ <form method="POST" action="/account/login/">
94
+ <input name="csrf_token" type="hidden" value="TOKEN000">
95
+ <input name="username" type="text" value="">
96
+ <input name="password" type="password" value="">
97
+ <button type="submit">Log in</button>
98
+ </form>
99
+ </body></html>"""
100
+
101
+ # Warehouse re-auth form, rendered when a destructive POST needs a password.
102
+ REAUTH_PAGE = """<!DOCTYPE html>
103
+ <html><body>
104
+ <form method="POST" action="/account/reauthenticate/">
105
+ <input name="csrf_token" type="hidden" value="TOKEN456">
106
+ <input name="next_route" type="hidden" value="manage.project.release">
107
+ <input name="next_route_matchdict" type="hidden"
108
+ value="{&quot;project_name&quot;: &quot;agentic-dev-loop&quot;,
109
+ &quot;version&quot;: &quot;0.15.7&quot;}">
110
+ <input name="next_route_query" type="hidden" value="{}">
111
+ <input name="password" type="password" value="">
112
+ <button type="submit">Confirm password</button>
113
+ </form>
114
+ </body></html>"""
115
+
116
+ # Warehouse two-factor form, rendered at login when the account has 2FA.
117
+ TWO_FACTOR_PAGE = """<!DOCTYPE html>
118
+ <html><body>
119
+ <form method="POST" action="/account/two-factor/">
120
+ <input name="csrf_token" type="hidden" value="TOKEN789">
121
+ <input name="totp_value" type="text" value="">
122
+ <button type="submit">Verify</button>
123
+ </form>
124
+ </body></html>"""
125
+
126
+
127
+ class TestVersionSelection:
128
+ def test_keep_survives_everything_else_is_selected(self):
129
+ plan = yank.versions_to_yank(REAL_RELEASES, "1.0.0a1")
130
+ assert "1.0.0a1" not in plan
131
+ assert "0.3.0" in plan
132
+ assert "0.10.0" in plan
133
+ assert "0.15.7" in plan
134
+ assert len(plan) == 3
135
+
136
+ def test_plan_is_oldest_first(self):
137
+ plan = yank.versions_to_yank(REAL_RELEASES, "1.0.0a1")
138
+ assert plan == sorted(plan, key=Version)
139
+
140
+ def test_multiple_keeps_all_survive(self):
141
+ plan = yank.versions_to_yank(REAL_RELEASES, "0.3.0,1.0.0a1")
142
+ assert "0.3.0" not in plan
143
+ assert "1.0.0a1" not in plan
144
+ assert plan == ["0.10.0", "0.15.7"]
145
+
146
+ def test_absent_keep_is_noted_and_ignored(self):
147
+ plan = yank.versions_to_yank(REAL_RELEASES, "1.0.0a1")
148
+ assert plan == ["0.3.0", "0.10.0", "0.15.7"]
149
+ assert yank.missing_keeps(REAL_RELEASES, "1.0.0a1") == ["1.0.0a1"]
150
+
151
+ def test_partially_missing_keep_list_keeps_the_rest(self):
152
+ plan = yank.versions_to_yank(REAL_RELEASES, "0.3.0,9.9.9")
153
+ assert "0.3.0" not in plan
154
+ assert "0.15.7" in plan
155
+ assert yank.missing_keeps(REAL_RELEASES, "0.3.0,9.9.9") == ["9.9.9"]
156
+
157
+ def test_empty_release_lists_are_skipped(self):
158
+ releases = {**REAL_RELEASES, "0.15.8": []}
159
+ plan = yank.versions_to_yank(releases, "1.0.0a1")
160
+ assert "0.15.8" not in plan
161
+
162
+ def test_keep_is_the_only_release(self):
163
+ plan = yank.versions_to_yank(
164
+ {"1.0.0a1": [{"filename": "agentic_dev_loop-1.0.0a1.tar.gz",
165
+ "yanked": False}]},
166
+ "1.0.0a1")
167
+ assert plan == []
168
+
169
+
170
+ class TestFormParsing:
171
+ def test_yank_form_is_found(self):
172
+ forms = yank.parse_forms(YANK_PAGE)
173
+ form = yank.find_form(forms, "confirm_yank_version")
174
+ assert form is not None
175
+ assert form["action"] == \
176
+ "/manage/project/agentic-dev-loop/release/0.15.7/"
177
+ assert form["inputs"]["csrf_token"] == "TOKEN123"
178
+
179
+ def test_per_file_delete_form_is_not_the_yank_form(self):
180
+ forms = yank.parse_forms(YANK_PAGE)
181
+ form = yank.find_form(forms, "confirm_yank_version")
182
+ assert form["inputs"]["csrf_token"] == "TOKEN123"
183
+ assert "confirm_delete_version" not in form["inputs"]
184
+
185
+ def test_already_yanked_detected_via_unyank_form(self):
186
+ assert yank.already_yanked(yank.parse_forms(UNYANK_PAGE))
187
+
188
+ def test_already_yanked_false_on_yank_page(self):
189
+ assert not yank.already_yanked(yank.parse_forms(YANK_PAGE))
190
+
191
+ def test_reauth_detection(self):
192
+ forms = yank.parse_forms(REAUTH_PAGE)
193
+ assert yank.needs_reauth(forms)
194
+ form = yank.find_form(forms, "password")
195
+ assert form is not None
196
+ assert form["inputs"]["next_route"] == "manage.project.release"
197
+ assert form["action"] == "/account/reauthenticate/"
198
+
199
+ def test_reauth_not_detected_on_plain_pages(self):
200
+ assert not yank.needs_reauth(yank.parse_forms(YANK_PAGE))
201
+
202
+ def test_totp_form_is_found(self):
203
+ forms = yank.parse_forms(TWO_FACTOR_PAGE)
204
+ form = yank.find_form(forms, "totp_value")
205
+ assert form is not None
206
+ assert form["inputs"]["csrf_token"] == "TOKEN789"
207
+
208
+ def test_entities_in_inputs_are_unescaped(self):
209
+ html = ('<form method="POST" action="/x?a=1&amp;b=2">'
210
+ '<input name="csrf_token" type="hidden" value="&quot;q&quot;">'
211
+ "</form>")
212
+ form = yank.parse_forms(html)[0]
213
+ assert form["action"] == "/x?a=1&b=2"
214
+ assert form["inputs"]["csrf_token"] == '"q"'
215
+
216
+
217
+ class TestPostHeaders:
218
+ def test_posts_carry_the_origin_header(self):
219
+ # Load-bearing: warehouse checks the CSRF origin on every POST and
220
+ # answers HTTP 400 when neither Origin nor Referer is present,
221
+ # before any credential validation happens. Sending the Origin
222
+ # header is what makes the login POST survive long enough to ask
223
+ # for credentials.
224
+ headers = yank.post_headers()
225
+ assert headers["Origin"] == "https://pypi.org"
226
+ assert headers["Content-Type"] == "application/x-www-form-urlencoded"
227
+ assert headers["User-Agent"].startswith("agentic-dev-loop")
228
+
229
+
230
+ class TestYankPostBody:
231
+ def test_body_carries_version_reason_and_csrf(self):
232
+ form = yank.find_form(yank.parse_forms(YANK_PAGE),
233
+ "confirm_yank_version")
234
+ body = yank.yank_post_body(form, "0.15.7", "superseded")
235
+ assert body["confirm_yank_version"] == "0.15.7"
236
+ assert body["yanked_reason"] == "superseded"
237
+ assert body["csrf_token"] == "TOKEN123"
238
+
239
+ def test_body_without_reason_sends_empty_string(self):
240
+ form = yank.find_form(yank.parse_forms(YANK_PAGE),
241
+ "confirm_yank_version")
242
+ body = yank.yank_post_body(form, "0.15.7", "")
243
+ assert body["confirm_yank_version"] == "0.15.7"
244
+ assert body["yanked_reason"] == ""
245
+ assert body["csrf_token"] == "TOKEN123"
246
+
247
+
248
+ class TestRedirectBackDetection:
249
+ def test_redirect_back_to_release_page_is_a_refusal(self):
250
+ location = "/manage/project/agentic-dev-loop/release/0.15.7/"
251
+ assert yank.yank_redirects_back(location, "agentic-dev-loop", "0.15.7")
252
+
253
+ def test_redirect_away_is_success(self):
254
+ location = "/manage/project/agentic-dev-loop/releases/"
255
+ assert not yank.yank_redirects_back(location, "agentic-dev-loop",
256
+ "0.15.7")
257
+
258
+ def test_absolute_location_is_parsed(self):
259
+ location = ("https://pypi.org/manage/project/agentic-dev-loop/"
260
+ "release/0.15.7/")
261
+ assert yank.yank_redirects_back(location, "agentic-dev-loop", "0.15.7")
262
+
263
+
264
+ class TestLoginPageDetection:
265
+ def test_login_page_detected(self):
266
+ assert yank.is_login_page(yank.parse_forms(LOGIN_PAGE))
267
+
268
+ def test_reauth_page_is_not_login_page(self):
269
+ assert not yank.is_login_page(yank.parse_forms(REAUTH_PAGE))
270
+
271
+ def test_yank_page_is_not_login_page(self):
272
+ assert not yank.is_login_page(yank.parse_forms(YANK_PAGE))
273
+
274
+
275
+ class TestPlanText:
276
+ def test_plan_names_every_version_to_yank(self):
277
+ text = yank.plan_text(REAL_RELEASES, "1.0.0a1", "agentic-dev-loop")
278
+ assert "agentic-dev-loop" in text
279
+ assert "keep: 1.0.0a1" in text
280
+ for version in ("0.3.0", "0.10.0", "0.15.7"):
281
+ assert version in text
282
+
283
+ def test_plan_notes_absent_keeps(self):
284
+ text = yank.plan_text(REAL_RELEASES, "0.3.0,9.9.9",
285
+ "agentic-dev-loop")
286
+ assert "note: keep '9.9.9' is not on PyPI" in text
287
+
288
+ def test_plan_notes_already_yanked_versions(self):
289
+ releases = {**REAL_RELEASES,
290
+ "0.15.0": [{"filename": "agentic_dev_loop-0.15.0.tar.gz",
291
+ "yanked": True}]}
292
+ text = yank.plan_text(releases, "1.0.0a1", "agentic-dev-loop")
293
+ assert "already fully yanked" in text
294
+ assert "0.15.0" in text
295
+
296
+
297
+ class TestDefaultKeep:
298
+ def test_default_keep_is_the_pyproject_version(self):
299
+ import tomllib
300
+
301
+ data = tomllib.loads(
302
+ (REPO_ROOT / "pyproject.toml").read_text(encoding="utf-8"))
303
+ assert yank.default_keep() == data["project"]["version"]
304
+
305
+
306
+ class TestDryRunCLI:
307
+ def test_dry_run_from_saved_dump_changes_nothing(self, tmp_path):
308
+ import json
309
+ import subprocess
310
+ import sys
311
+
312
+ dump = tmp_path / "dump.json"
313
+ dump.write_text(json.dumps({"releases": REAL_RELEASES}))
314
+ proc = subprocess.run(
315
+ [sys.executable, str(YANK), "--dry-run",
316
+ "--from-json", str(dump), "--keep", "1.0.0a1"],
317
+ capture_output=True, text=True,
318
+ )
319
+ assert proc.returncode == 0, proc.stderr
320
+ assert "dry run" in proc.stdout.lower()
321
+ assert "0.15.7" in proc.stdout
322
+ assert "0.3.0" in proc.stdout
323
+ assert "1.0.0a1" in proc.stdout
324
+
325
+ def test_dry_run_with_absent_keep_still_plans(self, tmp_path):
326
+ import json
327
+ import subprocess
328
+ import sys
329
+
330
+ dump = tmp_path / "dump.json"
331
+ dump.write_text(json.dumps({"releases": REAL_RELEASES}))
332
+ proc = subprocess.run(
333
+ [sys.executable, str(YANK), "--dry-run",
334
+ "--from-json", str(dump), "--keep", "9.9.9"],
335
+ capture_output=True, text=True,
336
+ )
337
+ assert proc.returncode == 0, proc.stderr
338
+ assert "not on PyPI" in proc.stdout
339
+ assert "0.15.7" in proc.stdout