claude-code-generator 0.6.1__tar.gz → 0.6.3__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 (274) hide show
  1. {claude_code_generator-0.6.1/src/claude_code_generator.egg-info → claude_code_generator-0.6.3}/PKG-INFO +1 -1
  2. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/pyproject.toml +1 -1
  3. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3/src/claude_code_generator.egg-info}/PKG-INFO +1 -1
  4. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/claude_code_generator.egg-info/SOURCES.txt +1 -0
  5. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/__init__.py +1 -1
  6. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/phase6_5_verify.py +90 -7
  7. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/prompts/__init__.py +3 -0
  8. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/prompts/prompt-phase-6-5-verify.md +6 -2
  9. claude_code_generator-0.6.3/tests/test_phase6_5_port_isolation.py +87 -0
  10. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase6_5_prompt.py +2 -0
  11. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_prompt_prefix_snapshots.py +1 -0
  12. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_prompt_prefix_stability.py +6 -0
  13. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_prompts.py +1 -0
  14. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_sica.py +1 -0
  15. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/LICENSE +0 -0
  16. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/README.md +0 -0
  17. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/setup.cfg +0 -0
  18. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/claude_code_generator.egg-info/dependency_links.txt +0 -0
  19. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/claude_code_generator.egg-info/entry_points.txt +0 -0
  20. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/claude_code_generator.egg-info/requires.txt +0 -0
  21. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/claude_code_generator.egg-info/top_level.txt +0 -0
  22. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/agents.py +0 -0
  23. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/archive_run.py +0 -0
  24. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/changelog.py +0 -0
  25. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/checklist.py +0 -0
  26. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/cli.py +0 -0
  27. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/commands/__init__.py +0 -0
  28. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/commands/_bench_io.py +0 -0
  29. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/commands/_crash_recovery.py +0 -0
  30. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/commands/_detect.py +0 -0
  31. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/commands/_dispatch.py +0 -0
  32. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/commands/_resume.py +0 -0
  33. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/commands/_validators.py +0 -0
  34. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/commands/bench.py +0 -0
  35. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/commands/bench_compare.py +0 -0
  36. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/commands/bench_export.py +0 -0
  37. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/commands/generate.py +0 -0
  38. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/commands/init.py +0 -0
  39. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/commands/optimize.py +0 -0
  40. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/commands/review.py +0 -0
  41. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/commands/status.py +0 -0
  42. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/effort.py +0 -0
  43. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/env.py +0 -0
  44. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/exceptions.py +0 -0
  45. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/gh/__init__.py +0 -0
  46. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/gh/actions.py +0 -0
  47. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/gh/core.py +0 -0
  48. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/gh/issues.py +0 -0
  49. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/gh/labels.py +0 -0
  50. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/gh/milestones.py +0 -0
  51. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/git_ops.py +0 -0
  52. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/logging_setup.py +0 -0
  53. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/manifest.py +0 -0
  54. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/memory.py +0 -0
  55. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/__init__.py +0 -0
  56. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/_cache_warmup.py +0 -0
  57. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/_client_lifecycle.py +0 -0
  58. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/_comments.py +0 -0
  59. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/_memory_writers.py +0 -0
  60. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/_phase5_precommit.py +0 -0
  61. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/agent_spec.py +0 -0
  62. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/criteria_oracle.py +0 -0
  63. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/cycle_loop.py +0 -0
  64. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/cycle_prompts.py +0 -0
  65. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/deviation_detector.py +0 -0
  66. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/failure_report.py +0 -0
  67. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/ollama_budget.py +0 -0
  68. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/phase0_complexity.py +0 -0
  69. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/phase1_plan.py +0 -0
  70. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/phase2_review.py +0 -0
  71. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/phase3_4_implement.py +0 -0
  72. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/phase5_closure.py +0 -0
  73. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/phase65_budget.py +0 -0
  74. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/phase6_test.py +0 -0
  75. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/phase7_commit.py +0 -0
  76. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/phase8_ci_classifier.py +0 -0
  77. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/phase8_finalization.py +0 -0
  78. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/phase8_local_verify.py +0 -0
  79. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/phase8_repair_loop.py +0 -0
  80. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/phase8_workflow_gen.py +0 -0
  81. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/scenario_builder.py +0 -0
  82. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/sica.py +0 -0
  83. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/tier_selector.py +0 -0
  84. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/tier_t3.py +0 -0
  85. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/tier_t4.py +0 -0
  86. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/orchestrator/vigil.py +0 -0
  87. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/preflight.py +0 -0
  88. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/prompts/hashes.py +0 -0
  89. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/prompts/prompt-cycle-specializer.md +0 -0
  90. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/prompts/prompt-optimize-requirements.md +0 -0
  91. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/prompts/prompt-phase-0-complexity.md +0 -0
  92. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/prompts/prompt-phase-1-planning.md +0 -0
  93. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/prompts/prompt-phase-2-batch-review.md +0 -0
  94. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/prompts/prompt-phase-3-implementation.md +0 -0
  95. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/prompts/prompt-phase-5-final-review.md +0 -0
  96. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/prompts/prompt-phase-6-test.md +0 -0
  97. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/prompts/prompt-phase-7-commit.md +0 -0
  98. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/prompts/prompt-phase-8-finalization.md +0 -0
  99. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/prompts/prompt-phase-8-repair.md +0 -0
  100. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/prompts/prompt-review.md +0 -0
  101. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/repo_info.py +0 -0
  102. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/requirements_structure.py +0 -0
  103. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/runner/__init__.py +0 -0
  104. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/runner/_telemetry.py +0 -0
  105. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/runner/batch.py +0 -0
  106. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/runner/cache_breakpoints.py +0 -0
  107. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/runner/codex_runner.py +0 -0
  108. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/runner/compaction_pause.py +0 -0
  109. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/runner/fake_runner.py +0 -0
  110. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/runner/gemini_runner.py +0 -0
  111. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/runner/mcp.py +0 -0
  112. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/runner/memory_tool.py +0 -0
  113. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/runner/message_parsing.py +0 -0
  114. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/runner/options.py +0 -0
  115. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/runner/phase_telemetry.py +0 -0
  116. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/runner/protocol.py +0 -0
  117. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/runner/rate_limit.py +0 -0
  118. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/runner/retry.py +0 -0
  119. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/runner/sdk_runner.py +0 -0
  120. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/runner/soft_reset.py +0 -0
  121. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/runner/state_guard.py +0 -0
  122. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/runner/subprocess_runner.py +0 -0
  123. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/runner/types.py +0 -0
  124. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/runner/utils.py +0 -0
  125. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/semver.py +0 -0
  126. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/state.py +0 -0
  127. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/state_retention.py +0 -0
  128. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/surface_scanner.py +0 -0
  129. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/templates/__init__.py +0 -0
  130. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/templates/angular.md +0 -0
  131. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/templates/base.md +0 -0
  132. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/templates/fastapi.md +0 -0
  133. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/templates/finance.md +0 -0
  134. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/templates/fullstack.md +0 -0
  135. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/templates/nestjs.md +0 -0
  136. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/src/code_generator/templates/python-cli.md +0 -0
  137. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_agent_spec.py +0 -0
  138. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_agents.py +0 -0
  139. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_archive_run.py +0 -0
  140. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_bench.py +0 -0
  141. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_bench_compare.py +0 -0
  142. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_bench_export.py +0 -0
  143. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_bench_fixture.py +0 -0
  144. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_bench_regression.py +0 -0
  145. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_cache_breakpoints.py +0 -0
  146. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_cache_ttl_ordering.py +0 -0
  147. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_cache_warmup.py +0 -0
  148. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_changelog.py +0 -0
  149. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_checklist.py +0 -0
  150. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_claude_md.py +0 -0
  151. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_cli_io_logging.py +0 -0
  152. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_client_lifecycle.py +0 -0
  153. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_codex_preflight.py +0 -0
  154. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_codex_runner.py +0 -0
  155. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_comments.py +0 -0
  156. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_commit_message.py +0 -0
  157. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_compaction_pause_handler.py +0 -0
  158. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_crash_recovery.py +0 -0
  159. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_criteria_oracle.py +0 -0
  160. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_cycle_loop.py +0 -0
  161. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_cycle_loop_multicycle.py +0 -0
  162. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_cycle_loop_phase6_5.py +0 -0
  163. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_cycle_loop_phase8.py +0 -0
  164. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_cycle_ollama_model.py +0 -0
  165. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_cycle_prompts.py +0 -0
  166. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_delta_planning.py +0 -0
  167. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_dependencies.py +0 -0
  168. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_detect.py +0 -0
  169. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_deviation_detector.py +0 -0
  170. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_dispatch_graph_report.py +0 -0
  171. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_docs_no_default_max_turns.py +0 -0
  172. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_docs_ollama_model_guide.py +0 -0
  173. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_docs_ollama_pro.py +0 -0
  174. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_effective_model_routing.py +0 -0
  175. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_effort.py +0 -0
  176. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_effort_routing_consistency.py +0 -0
  177. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_env.py +0 -0
  178. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_failure_report.py +0 -0
  179. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_gemini_preflight.py +0 -0
  180. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_gemini_runner.py +0 -0
  181. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_generate.py +0 -0
  182. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_generate_ollama.py +0 -0
  183. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_generate_resume.py +0 -0
  184. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_gh.py +0 -0
  185. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_gh_actions.py +0 -0
  186. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_gh_issues_create.py +0 -0
  187. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_gh_labels.py +0 -0
  188. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_gh_milestones.py +0 -0
  189. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_gh_repo_threading.py +0 -0
  190. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_gh_submodules.py +0 -0
  191. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_git_ops.py +0 -0
  192. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_git_ops_phase8.py +0 -0
  193. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_init.py +0 -0
  194. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_logging_setup.py +0 -0
  195. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_manifest.py +0 -0
  196. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_max_turns_cli_flag.py +0 -0
  197. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_mcp.py +0 -0
  198. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_memory.py +0 -0
  199. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_memory_tool.py +0 -0
  200. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_memory_writers.py +0 -0
  201. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_message_parsing.py +0 -0
  202. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_no_max_turns_in_call_sites.py +0 -0
  203. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_no_max_turns_literal.py +0 -0
  204. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_non_goals_grep_guard.py +0 -0
  205. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_ollama_budget.py +0 -0
  206. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_ollama_hardening.py +0 -0
  207. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_ollama_rate_limit.py +0 -0
  208. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_optimize.py +0 -0
  209. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_options.py +0 -0
  210. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase0.py +0 -0
  211. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase1.py +0 -0
  212. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase2.py +0 -0
  213. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase2_cache_regression.py +0 -0
  214. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase2_multicycle_token_reduction.py +0 -0
  215. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase2_token_reduction.py +0 -0
  216. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase3_4.py +0 -0
  217. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase5.py +0 -0
  218. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase5_precommit.py +0 -0
  219. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase6.py +0 -0
  220. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase65_budget.py +0 -0
  221. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase6_5_integration.py +0 -0
  222. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase6_5_integration_cycle2.py +0 -0
  223. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase6_5_prompt_snapshot.py +0 -0
  224. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase6_5_run.py +0 -0
  225. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase6_5_state.py +0 -0
  226. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase6_5_tiers.py +0 -0
  227. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase7.py +0 -0
  228. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase8_access_control.py +0 -0
  229. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase8_changelog.py +0 -0
  230. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase8_ci_classifier.py +0 -0
  231. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase8_finalization.py +0 -0
  232. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase8_local_verify.py +0 -0
  233. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase8_prompt_snapshots.py +0 -0
  234. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase8_prompts.py +0 -0
  235. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase8_repair_loop.py +0 -0
  236. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase8_repair_sota.py +0 -0
  237. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase8_semver.py +0 -0
  238. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase8_state.py +0 -0
  239. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase8_status.py +0 -0
  240. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase8_surface_scanner.py +0 -0
  241. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase8_trigger.py +0 -0
  242. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase8_workflow_gen.py +0 -0
  243. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase_mcp_regression.py +0 -0
  244. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase_telemetry.py +0 -0
  245. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_phase_token_logging.py +0 -0
  246. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_preflight.py +0 -0
  247. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_preflight_ollama.py +0 -0
  248. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_prompt_drift.py +0 -0
  249. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_rate_limit.py +0 -0
  250. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_repo_info.py +0 -0
  251. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_requirements_structure.py +0 -0
  252. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_resume.py +0 -0
  253. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_retry.py +0 -0
  254. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_review.py +0 -0
  255. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_runner_protocol.py +0 -0
  256. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_runner_protocol_annotations.py +0 -0
  257. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_runner_types.py +0 -0
  258. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_runner_utils.py +0 -0
  259. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_scenario_builder.py +0 -0
  260. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_sdk_runner.py +0 -0
  261. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_sdk_runner_shared.py +0 -0
  262. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_session_mode.py +0 -0
  263. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_state.py +0 -0
  264. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_state_guard.py +0 -0
  265. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_state_retention.py +0 -0
  266. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_status.py +0 -0
  267. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_subprocess_runner.py +0 -0
  268. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_telemetry.py +0 -0
  269. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_tier_selector.py +0 -0
  270. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_tier_t3.py +0 -0
  271. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_tier_t3_pass_rate.py +0 -0
  272. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_tier_t4.py +0 -0
  273. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_version.py +0 -0
  274. {claude_code_generator-0.6.1 → claude_code_generator-0.6.3}/tests/test_vigil.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-code-generator
3
- Version: 0.6.1
3
+ Version: 0.6.3
4
4
  Summary: Orchestrator CLI that drives Claude Code end-to-end to generate whole projects from a requirements.md file.
5
5
  Author: Silvio Baratto
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "claude-code-generator"
7
- version = "0.6.1"
7
+ version = "0.6.3"
8
8
  description = "Orchestrator CLI that drives Claude Code end-to-end to generate whole projects from a requirements.md file."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-code-generator
3
- Version: 0.6.1
3
+ Version: 0.6.3
4
4
  Summary: Orchestrator CLI that drives Claude Code end-to-end to generate whole projects from a requirements.md file.
5
5
  Author: Silvio Baratto
6
6
  License: MIT
@@ -212,6 +212,7 @@ tests/test_phase6.py
212
212
  tests/test_phase65_budget.py
213
213
  tests/test_phase6_5_integration.py
214
214
  tests/test_phase6_5_integration_cycle2.py
215
+ tests/test_phase6_5_port_isolation.py
215
216
  tests/test_phase6_5_prompt.py
216
217
  tests/test_phase6_5_prompt_snapshot.py
217
218
  tests/test_phase6_5_run.py
@@ -1,3 +1,3 @@
1
1
  """code-generator: orchestrator CLI for end-to-end project generation."""
2
2
 
3
- __version__ = "0.6.1"
3
+ __version__ = "0.6.3"
@@ -32,7 +32,7 @@ import socket
32
32
  import subprocess
33
33
  import sys
34
34
  import time
35
- from dataclasses import dataclass, field
35
+ from dataclasses import dataclass, field, replace
36
36
  from typing import TYPE_CHECKING, Any
37
37
 
38
38
  import httpx
@@ -348,6 +348,48 @@ def _spawn_child(entry_cmd: list[str], cwd: Path, env: Mapping[str, str]) -> sub
348
348
  )
349
349
 
350
350
 
351
+ def _alloc_free_port() -> int:
352
+ """Bind ``127.0.0.1:0`` to obtain an OS-assigned free TCP port, then
353
+ release it.
354
+
355
+ Phase 6.5 must not hardcode ``:8000`` — co-resident tooling (e.g. the
356
+ claude-mem plugin's ``chroma run``) squats well-known ports and returns
357
+ 404 to every health probe, burning the entire repair-retry budget on a
358
+ purely environmental collision. An ephemeral port makes that collision
359
+ structurally impossible.
360
+
361
+ There is a benign TOCTOU window between release here and the app binding
362
+ the port; :func:`_ensure_verify_port` closes it by re-checking (and
363
+ rerolling) immediately before each attempt.
364
+ """
365
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
366
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
367
+ sock.bind(("127.0.0.1", 0))
368
+ return int(sock.getsockname()[1])
369
+
370
+
371
+ def _ensure_verify_port(port: int, *, logger: logging.Logger) -> int:
372
+ """Pre-flight port guard (#2).
373
+
374
+ If *port* was claimed by a foreign listener between attempts (a plugin
375
+ rebinding, a leaked uvicorn from a prior run), reroll to a fresh free
376
+ port instead of letting the squatter fail a tier and consume a retry.
377
+ A genuine app bug still fails the tier on its own port; only
378
+ environmental collisions are absorbed here.
379
+ """
380
+ if not _port_open(port):
381
+ return port
382
+ new_port = _alloc_free_port()
383
+ logger.warning(
384
+ "Phase 6.5 verify port %d occupied by a foreign listener before the "
385
+ "attempt began; rerolled to %d so the retry budget is not spent on "
386
+ "an environmental port collision.",
387
+ port,
388
+ new_port,
389
+ )
390
+ return new_port
391
+
392
+
351
393
  def _port_open(port: int, host: str = "127.0.0.1", timeout_s: float = 0.25) -> bool:
352
394
  try:
353
395
  with socket.create_connection((host, port), timeout=timeout_s):
@@ -654,6 +696,10 @@ async def run(
654
696
  budget = Phase65BudgetTracker(wall_budget_s=wall_budget_s, turn_budget=turn_budget)
655
697
  budget.start()
656
698
 
699
+ # Ephemeral verification port (#1): allocate once so base_url stays
700
+ # stable across the retry loop; re-checked per attempt by the guard (#2).
701
+ verify_port = _alloc_free_port()
702
+
657
703
  for attempt in range(max_retries):
658
704
  budget.check(turns_consumed=attempt, logger=logger)
659
705
  progress = _begin_attempt(target, progress, attempt)
@@ -661,6 +707,7 @@ async def run(
661
707
  if attempt > 0:
662
708
  _soft_reset_for_repair(project_dir, attempt + 1)
663
709
 
710
+ verify_port = _ensure_verify_port(verify_port, logger=logger)
664
711
  outcome = _run_tiers(
665
712
  project_dir,
666
713
  state,
@@ -669,6 +716,7 @@ async def run(
669
716
  progress,
670
717
  attempt,
671
718
  target,
719
+ port=verify_port,
672
720
  logger=logger,
673
721
  detector=detector,
674
722
  )
@@ -691,6 +739,7 @@ async def run(
691
739
  max_turns=max_turns,
692
740
  attempt=attempt,
693
741
  target=target,
742
+ port=verify_port,
694
743
  )
695
744
 
696
745
  raise VerificationFailed(
@@ -722,6 +771,7 @@ def _run_tiers(
722
771
  attempt: int,
723
772
  target: Any,
724
773
  *,
774
+ port: int,
725
775
  logger: logging.Logger,
726
776
  detector: DeviationDetector | None = None,
727
777
  ) -> TierResult | None:
@@ -731,13 +781,27 @@ def _run_tiers(
731
781
  :func:`tier_selector.select_tiers` then iterates ``spec.tiers``,
732
782
  skipping any tier with ``enabled=False`` and recording the
733
783
  skip reason on ``Phase65Progress``.
784
+
785
+ *port* is the ephemeral free port allocated by :func:`run` (#1). It is
786
+ woven into ``base_url``, the subprocess env (``PORT`` / ``UVICORN_PORT``
787
+ / ``APP_PORT``), and the persisted spec so both the programmatic tier
788
+ runners and the SDK repair agent bind the same collision-free port.
734
789
  """
735
- env = build_agent_env()
790
+ base_url = f"http://127.0.0.1:{port}"
791
+ # The SDK repair agent does not inherit this env (its credentials are
792
+ # locked at shared-client open time); it learns the port from the
793
+ # persisted spec's base_url/T1.port instead. These vars steer the
794
+ # programmatic T0/T2 subprocesses and any app that honours $PORT.
795
+ env = {
796
+ **build_agent_env(),
797
+ "PORT": str(port),
798
+ "UVICORN_PORT": str(port),
799
+ "APP_PORT": str(port),
800
+ }
736
801
  requirements_path = project_dir / ".code-generator" / "requirements.md"
737
802
  endpoints = _load_endpoints(requirements_path)
738
- base_url = "http://127.0.0.1:8000"
739
803
 
740
- spec = _persist_spec_for_attempt(project_dir, requirements_path)
804
+ spec = _persist_spec_for_attempt(project_dir, requirements_path, port)
741
805
  _record_skips(target, progress, spec, attempt, logger=logger)
742
806
  save_state(state_path, state)
743
807
 
@@ -759,11 +823,15 @@ def _run_tiers(
759
823
  return None
760
824
 
761
825
 
762
- def _persist_spec_for_attempt(project_dir: Path, requirements_path: Path) -> AgentSpec:
826
+ def _persist_spec_for_attempt(project_dir: Path, requirements_path: Path, port: int) -> AgentSpec:
763
827
  """Build the ``AgentSpec`` for this run and atomically persist it.
764
828
 
765
829
  The spec is the single source of truth consumed by :func:`_run_tiers`
766
- and any later replay tooling.
830
+ *and* the SDK repair agent (the prompt instructs it to trust the spec's
831
+ declared ports). ``build_agent_spec`` stays pure and deterministic
832
+ (default ``:8000``); the ephemeral *port* (#1) is layered on here via
833
+ :func:`dataclasses.replace` so the agent boots the app on a
834
+ collision-free port instead of the well-known ``:8000``.
767
835
  """
768
836
  tiers = select_tiers(project_dir, requirements_path)
769
837
  requirements_text = (
@@ -771,6 +839,13 @@ def _persist_spec_for_attempt(project_dir: Path, requirements_path: Path) -> Age
771
839
  )
772
840
  flags = {tier.tier_id: tier.enabled for tier in tiers if tier.tier_id in {"T3", "T4"}}
773
841
  spec = build_agent_spec(project_dir, requirements_text, flags)
842
+ spec = replace(
843
+ spec,
844
+ base_url=f"http://127.0.0.1:{port}",
845
+ tiers=tuple(
846
+ replace(tier, port=port) if tier.port is not None else tier for tier in spec.tiers
847
+ ),
848
+ )
774
849
  persist_agent_spec(spec, project_dir / ".code-generator")
775
850
  return spec
776
851
 
@@ -949,8 +1024,15 @@ async def _run_repair_pass(
949
1024
  max_turns: int | None,
950
1025
  attempt: int,
951
1026
  target: Any,
1027
+ port: int,
952
1028
  ) -> None:
953
- """Dispatch the Sonnet 4.6 repair pass via the per-cycle shared client (#275)."""
1029
+ """Dispatch the Sonnet 4.6 repair pass via the per-cycle shared client (#275).
1030
+
1031
+ *port* is the ephemeral verify port. It is injected into the prompt as
1032
+ ``VERIFY_BASE_URL`` so the repair agent binds the app to the exact
1033
+ host:port the orchestrator's authoritative T2 probe hits — the agent
1034
+ inventing its own port was the entire failure mode this guards against.
1035
+ """
954
1036
  del runner_module # rate_limit signal is replayed via run_with_shared_client
955
1037
  requirements_path = project_dir / ".code-generator" / "requirements.md"
956
1038
  spec_path = project_dir / ".code-generator" / "phase6_5_spec.json"
@@ -968,6 +1050,7 @@ async def _run_repair_pass(
968
1050
  ORACLE_REPORT=_load_oracle_report_text(requirements_path),
969
1051
  TIERS_ACTIVE=tiers_active,
970
1052
  TIERS_SKIPPED=tiers_skipped,
1053
+ VERIFY_BASE_URL=f"http://127.0.0.1:{port}",
971
1054
  )
972
1055
  effort_level = _effort.resolve_effort(
973
1056
  phase=66, complexity="", issue_labels=[], retry_count=attempt
@@ -68,6 +68,9 @@ PLACEHOLDER_SPEC: dict[str, frozenset[str]] = {
68
68
  "AGENT_SPEC_PATH",
69
69
  "TIERS_ACTIVE",
70
70
  "TIERS_SKIPPED",
71
+ # Ephemeral verify-port isolation: the exact host:port the
72
+ # orchestrator probes; the repair agent must bind precisely this.
73
+ "VERIFY_BASE_URL",
71
74
  }
72
75
  ),
73
76
  "prompt-phase-7-commit.md": frozenset(
@@ -14,6 +14,8 @@ Verification runs in five tiers, lightest first; each is a hard gate on the next
14
14
  - **T0 — Build.** Run the canonical build command for the detected stack. Compile errors here block T1.
15
15
  - **T1 — Boot smoke.** Start the application or its main entry point. Watch for crash-on-boot, missing dependencies, unreachable migrations, or import-time errors. Time-bound: a successful boot is detected by either an explicit "ready" log line or the process surviving for `BOOT_SMOKE_WINDOW_SECONDS` (default 8 seconds) without exiting.
16
16
  - **T2 — API contract.** When the project exposes HTTP routes, replay the acceptance-criteria endpoint list against the running service from T1. Each route must return its documented status code; response-shape checks are advisory unless the criterion specifies them.
17
+
18
+ > **Mandatory bind address.** The orchestrator runs its own authoritative T2 probe against **exactly** the URL in `{VERIFY_BASE_URL}` (see `## Volatile Context`) — nothing else. The service you boot in T1 **must listen on that exact host and port**. Do not invent your own port, do not "pick a free one", do not switch the host. A self-reported `=== TIER 2: PASS ===` against any other port is meaningless: the orchestrator only sees `{VERIFY_BASE_URL}`, so a different port is an automatic T2 failure no matter what your local curl shows.
17
19
  - **T3 — Browser end-to-end (Playwright MCP).** When the project ships a frontend, drive the running app through the Playwright MCP server. Execute every acceptance flow grouped under the relevant `## Scope` subsection. On any scenario failure, persist a screenshot to `.code-generator/screenshots/<scenario>.png`, drain the browser console messages, and attach both to the failure report. Skip cleanly with a single-line reason when no frontend is in scope.
18
20
  - **T4 — Container lifecycle.** When the project ships `docker-compose.yml`, `docker-compose.yaml`, or a bare `Dockerfile`, bring the container(s) up (`docker compose up -d --build` or `docker build && docker run`), wait for the health endpoint, replay the T2 contract suite against the containerized service, then tear down via `try/finally` on every code path. Skip cleanly with a logged reason when no docker manifest exists.
19
21
 
@@ -27,7 +29,7 @@ Verification runs in five tiers, lightest first; each is a hard gate on the next
27
29
 
28
30
  2. **Emit a tier plan.** Before running anything, write a short plan covering:
29
31
  - T0 build command (string, no chaining).
30
- - T1 entry-point command, expected listening port (if applicable), and the readiness signal (log substring or, by default, the boot-window timer).
32
+ - T1 entry-point command. It **must bind the exact host and port in `{VERIFY_BASE_URL}`** (e.g. `--host 127.0.0.1 --port <that-port>`), plus the readiness signal (log substring or, by default, the boot-window timer). The port is not yours to choose — it is fixed by `{VERIFY_BASE_URL}` and the persisted `AgentSpec`.
31
33
  - T2 endpoint list: one row per `(METHOD, PATH, EXPECTED_STATUS)` extracted from the acceptance criteria. When no HTTP routes are extractable, mark T2 as `SKIP` — this is not a failure.
32
34
 
33
35
  3. **Run the tiers.** For each tier in order:
@@ -56,6 +58,7 @@ Verification runs in five tiers, lightest first; each is a hard gate on the next
56
58
  - **Do not modify or delete tests added by Phase 6.** The unit tests are now part of the contract; if a test fails after a repair edit, the patch is wrong, not the test.
57
59
  - **Never touch `state.json`.** It is owned by the orchestrator and made read-only during your run.
58
60
  - **Never touch `.code-generator/checklists/`.** Tick decisions are computed in Python from your exit code, not by editing the JSON.
61
+ - **Bind the exact `{VERIFY_BASE_URL}` host:port.** This is non-negotiable and overrides any port in `requirements.md`, a Dockerfile, or your own judgement. The orchestrator's authoritative probe hits only `{VERIFY_BASE_URL}`; a service on any other port fails T2 regardless of local curl output. If the app cannot bind that port, fix the app's port configuration (env var, CLI flag, settings) — never relocate the service to a different port to make a local check pass.
59
62
  - **Run long-running daemons in the background** — wrap them with `nohup`, `&`, or an equivalent so the verification harness can reclaim the shell, and tear them down via the PID you captured.
60
63
  - Stay inside the tiered loop: do not escalate effort by editing this prompt or invoking other agents.
61
64
  - Keep repairs minimal — edit the smallest scope that fixes the failing tier; refactors are out of scope here.
@@ -79,7 +82,7 @@ A first-attempt run on a FastAPI project where T0 passes and T1 fails:
79
82
  <example>
80
83
  Tier plan:
81
84
  T0: pip install -e .
82
- T1: uvicorn app.main:app --port 8000 (ready = "Application startup complete" or 8s window)
85
+ T1: uvicorn app.main:app --host 127.0.0.1 --port <VERIFY_BASE_URL port> (ready = "Application startup complete" or 8s window)
83
86
  T2: GET /health -> 200; POST /items -> 201
84
87
  T3: SKIP (no frontend)
85
88
  T4: SKIP (no docker manifest)
@@ -101,6 +104,7 @@ Still red: T1 (and dependent T2). Repair this attempt; T3/T4 remain SKIP.
101
104
 
102
105
  ## Volatile Context
103
106
 
107
+ - **Mandatory bind address (orchestrator probes EXACTLY this, nothing else):** {VERIFY_BASE_URL}
104
108
  - **Project directory:** {PROJECT_DIR}
105
109
  - **Requirements file:** {REQUIREMENTS_MD}
106
110
  - **Detected stack:** {DETECTED_STACK}
@@ -0,0 +1,87 @@
1
+ """Phase 6.5 ephemeral-port isolation (#1) and pre-flight port guard (#2).
2
+
3
+ Regression cover for the well-known-port collision that exhausted the
4
+ repair-retry budget: a co-resident ``chroma run`` (claude-mem plugin)
5
+ squatting ``:8000`` returned 404 to every health probe, so attempts 1+2
6
+ failed environmentally and attempt 3's real fix landed after the budget
7
+ was already spent. The fix moves verification onto an OS-assigned free
8
+ port and rerolls if that port is squatted before an attempt starts.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import logging
14
+ import socket
15
+ from pathlib import Path
16
+
17
+ from code_generator.orchestrator.agent_spec import load_agent_spec
18
+ from code_generator.orchestrator.phase6_5_verify import (
19
+ _alloc_free_port,
20
+ _ensure_verify_port,
21
+ _persist_spec_for_attempt,
22
+ )
23
+
24
+ _LOGGER = logging.getLogger("test.phase6_5.port_isolation")
25
+
26
+
27
+ def _make_project(tmp_path: Path) -> Path:
28
+ project_dir = tmp_path / "project"
29
+ (project_dir / ".code-generator").mkdir(parents=True)
30
+ (project_dir / ".code-generator" / "requirements.md").write_text(
31
+ "# Demo\n\n## Tech Stack\n\nFastAPI\n", encoding="utf-8"
32
+ )
33
+ return project_dir
34
+
35
+
36
+ def test_alloc_free_port_returns_a_bindable_ephemeral_port() -> None:
37
+ port = _alloc_free_port()
38
+ assert 1024 < port < 65536
39
+ # Released back to the OS — re-bindable, proving it was genuinely free.
40
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
41
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
42
+ sock.bind(("127.0.0.1", port))
43
+
44
+
45
+ def test_persisted_spec_carries_ephemeral_port_not_8000(tmp_path: Path) -> None:
46
+ project_dir = _make_project(tmp_path)
47
+ port = _alloc_free_port()
48
+
49
+ _persist_spec_for_attempt(
50
+ project_dir, project_dir / ".code-generator" / "requirements.md", port
51
+ )
52
+
53
+ spec = load_agent_spec(project_dir / ".code-generator")
54
+ assert spec.base_url == f"http://127.0.0.1:{port}"
55
+ assert spec.base_url != "http://127.0.0.1:8000"
56
+ # Every tier that declares a port (T1) is rewritten to the free port —
57
+ # this is the field the SDK repair agent reads to boot the app.
58
+ ported = [t for t in spec.tiers if t.port is not None]
59
+ assert ported, "expected at least one tier with an explicit port (T1)"
60
+ assert all(t.port == port for t in ported)
61
+
62
+
63
+ def test_guard_passes_through_a_free_port() -> None:
64
+ port = _alloc_free_port()
65
+ assert _ensure_verify_port(port, logger=_LOGGER) == port
66
+
67
+
68
+ def test_guard_rerolls_when_port_is_squatted(caplog) -> None:
69
+ """A foreign listener on the chosen port → reroll, never reuse it.
70
+
71
+ This is the chroma-on-:8000 scenario in miniature: the guard must
72
+ hand back a *different*, free port so the squatter cannot fail a
73
+ tier and burn a retry.
74
+ """
75
+ squatter = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
76
+ squatter.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
77
+ squatter.bind(("127.0.0.1", 0))
78
+ squatter.listen(1)
79
+ squatted_port = squatter.getsockname()[1]
80
+ try:
81
+ with caplog.at_level(logging.WARNING):
82
+ rerolled = _ensure_verify_port(squatted_port, logger=_LOGGER)
83
+ assert rerolled != squatted_port
84
+ assert 1024 < rerolled < 65536
85
+ assert "rerolled" in caplog.text
86
+ finally:
87
+ squatter.close()
@@ -20,6 +20,7 @@ _EXPECTED_PLACEHOLDERS = frozenset(
20
20
  "AGENT_SPEC_PATH",
21
21
  "TIERS_ACTIVE",
22
22
  "TIERS_SKIPPED",
23
+ "VERIFY_BASE_URL",
23
24
  }
24
25
  )
25
26
 
@@ -37,6 +38,7 @@ def _valid_kwargs() -> dict[str, str]:
37
38
  "AGENT_SPEC_PATH": "/tmp/proj/.code-generator/phase6_5_spec.json",
38
39
  "TIERS_ACTIVE": "T0,T1,T2",
39
40
  "TIERS_SKIPPED": "T3 (no frontend), T4 (no docker)",
41
+ "VERIFY_BASE_URL": "http://127.0.0.1:59568",
40
42
  }
41
43
 
42
44
 
@@ -82,6 +82,7 @@ PLACEHOLDER_SNAPSHOT_FIXTURES: dict[str, str] = {
82
82
  "AGENT_SPEC_PATH": ".code-generator/phase6_5_spec.json",
83
83
  "TIERS_ACTIVE": "T0,T1,T2",
84
84
  "TIERS_SKIPPED": "",
85
+ "VERIFY_BASE_URL": "http://127.0.0.1:PORT",
85
86
  "CI_LOGS": "",
86
87
  "FAILING_JOBS": "",
87
88
  "MAX_ATTEMPTS": "5",
@@ -175,6 +175,10 @@ _FIXTURES: dict[str, list[dict[str, str]]] = {
175
175
  "AGENT_SPEC_PATH": "/tmp/proj-a/.code-generator/phase6_5_spec.json",
176
176
  "TIERS_ACTIVE": "T0,T1,T2",
177
177
  "TIERS_SKIPPED": "T3 (no frontend), T4 (no docker)",
178
+ # Identical across all three fixtures: VERIFY_BASE_URL appears in
179
+ # the cached prefix (T2 section), so the 2048-byte stability
180
+ # contract requires the same value in every variant.
181
+ "VERIFY_BASE_URL": "http://127.0.0.1:PORT",
178
182
  },
179
183
  {
180
184
  "PROJECT_DIR": "/srv/proj-b",
@@ -188,6 +192,7 @@ _FIXTURES: dict[str, list[dict[str, str]]] = {
188
192
  "AGENT_SPEC_PATH": "/srv/proj-b/.code-generator/phase6_5_spec.json",
189
193
  "TIERS_ACTIVE": "T0,T1,T2,T4",
190
194
  "TIERS_SKIPPED": "T3 (no frontend)",
195
+ "VERIFY_BASE_URL": "http://127.0.0.1:PORT",
191
196
  },
192
197
  {
193
198
  "PROJECT_DIR": "/work/proj-c",
@@ -201,6 +206,7 @@ _FIXTURES: dict[str, list[dict[str, str]]] = {
201
206
  "AGENT_SPEC_PATH": "/work/proj-c/.code-generator/phase6_5_spec.json",
202
207
  "TIERS_ACTIVE": "T0,T3",
203
208
  "TIERS_SKIPPED": "T4 (no docker)",
209
+ "VERIFY_BASE_URL": "http://127.0.0.1:PORT",
204
210
  },
205
211
  ],
206
212
  "prompt-phase-7-commit.md": [
@@ -52,6 +52,7 @@ _EXPECTED_FILES = {
52
52
  "AGENT_SPEC_PATH",
53
53
  "TIERS_ACTIVE",
54
54
  "TIERS_SKIPPED",
55
+ "VERIFY_BASE_URL",
55
56
  }
56
57
  ),
57
58
  "prompt-phase-7-commit.md": frozenset(
@@ -255,6 +255,7 @@ async def test_when_phase6_5_repair_runs_then_routes_through_sica(
255
255
  max_turns=None,
256
256
  attempt=0,
257
257
  target=target,
258
+ port=59568,
258
259
  )
259
260
 
260
261
  assert captured_calls