claude-code-generator 0.6.3__tar.gz → 0.6.4__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 (277) hide show
  1. {claude_code_generator-0.6.3/src/claude_code_generator.egg-info → claude_code_generator-0.6.4}/PKG-INFO +1 -1
  2. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/pyproject.toml +1 -1
  3. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4/src/claude_code_generator.egg-info}/PKG-INFO +1 -1
  4. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/claude_code_generator.egg-info/SOURCES.txt +3 -0
  5. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/git_ops.py +29 -0
  6. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase6_5_verify.py +121 -4
  7. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase7_commit.py +7 -2
  8. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase8_finalization.py +65 -16
  9. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase8_workflow_gen.py +92 -0
  10. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/tier_t4.py +159 -32
  11. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/prompt-phase-3-implementation.md +1 -0
  12. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/prompt-phase-6-5-verify.md +2 -1
  13. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/prompt-phase-6-test.md +1 -0
  14. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/prompt-phase-7-commit.md +14 -5
  15. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_git_ops_phase8.py +29 -0
  16. claude_code_generator-0.6.4/tests/test_phase6_5_endpoint_source.py +64 -0
  17. claude_code_generator-0.6.4/tests/test_phase6_5_t4_timeout.py +68 -0
  18. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase7.py +6 -6
  19. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_finalization.py +48 -0
  20. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_workflow_gen.py +74 -0
  21. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_tier_t4.py +53 -1
  22. claude_code_generator-0.6.4/tests/test_tier_t4_port_discovery.py +90 -0
  23. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/LICENSE +0 -0
  24. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/README.md +0 -0
  25. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/setup.cfg +0 -0
  26. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/claude_code_generator.egg-info/dependency_links.txt +0 -0
  27. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/claude_code_generator.egg-info/entry_points.txt +0 -0
  28. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/claude_code_generator.egg-info/requires.txt +0 -0
  29. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/claude_code_generator.egg-info/top_level.txt +0 -0
  30. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/__init__.py +0 -0
  31. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/agents.py +0 -0
  32. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/archive_run.py +0 -0
  33. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/changelog.py +0 -0
  34. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/checklist.py +0 -0
  35. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/cli.py +0 -0
  36. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/__init__.py +0 -0
  37. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/_bench_io.py +0 -0
  38. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/_crash_recovery.py +0 -0
  39. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/_detect.py +0 -0
  40. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/_dispatch.py +0 -0
  41. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/_resume.py +0 -0
  42. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/_validators.py +0 -0
  43. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/bench.py +0 -0
  44. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/bench_compare.py +0 -0
  45. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/bench_export.py +0 -0
  46. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/generate.py +0 -0
  47. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/init.py +0 -0
  48. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/optimize.py +0 -0
  49. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/review.py +0 -0
  50. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/status.py +0 -0
  51. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/effort.py +0 -0
  52. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/env.py +0 -0
  53. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/exceptions.py +0 -0
  54. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/gh/__init__.py +0 -0
  55. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/gh/actions.py +0 -0
  56. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/gh/core.py +0 -0
  57. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/gh/issues.py +0 -0
  58. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/gh/labels.py +0 -0
  59. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/gh/milestones.py +0 -0
  60. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/logging_setup.py +0 -0
  61. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/manifest.py +0 -0
  62. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/memory.py +0 -0
  63. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/__init__.py +0 -0
  64. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/_cache_warmup.py +0 -0
  65. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/_client_lifecycle.py +0 -0
  66. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/_comments.py +0 -0
  67. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/_memory_writers.py +0 -0
  68. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/_phase5_precommit.py +0 -0
  69. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/agent_spec.py +0 -0
  70. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/criteria_oracle.py +0 -0
  71. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/cycle_loop.py +0 -0
  72. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/cycle_prompts.py +0 -0
  73. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/deviation_detector.py +0 -0
  74. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/failure_report.py +0 -0
  75. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/ollama_budget.py +0 -0
  76. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase0_complexity.py +0 -0
  77. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase1_plan.py +0 -0
  78. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase2_review.py +0 -0
  79. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase3_4_implement.py +0 -0
  80. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase5_closure.py +0 -0
  81. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase65_budget.py +0 -0
  82. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase6_test.py +0 -0
  83. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase8_ci_classifier.py +0 -0
  84. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase8_local_verify.py +0 -0
  85. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase8_repair_loop.py +0 -0
  86. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/scenario_builder.py +0 -0
  87. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/sica.py +0 -0
  88. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/tier_selector.py +0 -0
  89. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/tier_t3.py +0 -0
  90. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/vigil.py +0 -0
  91. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/preflight.py +0 -0
  92. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/__init__.py +0 -0
  93. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/hashes.py +0 -0
  94. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/prompt-cycle-specializer.md +0 -0
  95. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/prompt-optimize-requirements.md +0 -0
  96. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/prompt-phase-0-complexity.md +0 -0
  97. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/prompt-phase-1-planning.md +0 -0
  98. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/prompt-phase-2-batch-review.md +0 -0
  99. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/prompt-phase-5-final-review.md +0 -0
  100. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/prompt-phase-8-finalization.md +0 -0
  101. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/prompt-phase-8-repair.md +0 -0
  102. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/prompt-review.md +0 -0
  103. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/repo_info.py +0 -0
  104. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/requirements_structure.py +0 -0
  105. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/__init__.py +0 -0
  106. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/_telemetry.py +0 -0
  107. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/batch.py +0 -0
  108. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/cache_breakpoints.py +0 -0
  109. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/codex_runner.py +0 -0
  110. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/compaction_pause.py +0 -0
  111. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/fake_runner.py +0 -0
  112. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/gemini_runner.py +0 -0
  113. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/mcp.py +0 -0
  114. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/memory_tool.py +0 -0
  115. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/message_parsing.py +0 -0
  116. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/options.py +0 -0
  117. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/phase_telemetry.py +0 -0
  118. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/protocol.py +0 -0
  119. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/rate_limit.py +0 -0
  120. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/retry.py +0 -0
  121. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/sdk_runner.py +0 -0
  122. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/soft_reset.py +0 -0
  123. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/state_guard.py +0 -0
  124. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/subprocess_runner.py +0 -0
  125. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/types.py +0 -0
  126. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/utils.py +0 -0
  127. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/semver.py +0 -0
  128. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/state.py +0 -0
  129. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/state_retention.py +0 -0
  130. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/surface_scanner.py +0 -0
  131. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/templates/__init__.py +0 -0
  132. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/templates/angular.md +0 -0
  133. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/templates/base.md +0 -0
  134. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/templates/fastapi.md +0 -0
  135. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/templates/finance.md +0 -0
  136. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/templates/fullstack.md +0 -0
  137. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/templates/nestjs.md +0 -0
  138. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/templates/python-cli.md +0 -0
  139. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_agent_spec.py +0 -0
  140. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_agents.py +0 -0
  141. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_archive_run.py +0 -0
  142. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_bench.py +0 -0
  143. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_bench_compare.py +0 -0
  144. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_bench_export.py +0 -0
  145. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_bench_fixture.py +0 -0
  146. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_bench_regression.py +0 -0
  147. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_cache_breakpoints.py +0 -0
  148. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_cache_ttl_ordering.py +0 -0
  149. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_cache_warmup.py +0 -0
  150. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_changelog.py +0 -0
  151. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_checklist.py +0 -0
  152. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_claude_md.py +0 -0
  153. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_cli_io_logging.py +0 -0
  154. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_client_lifecycle.py +0 -0
  155. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_codex_preflight.py +0 -0
  156. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_codex_runner.py +0 -0
  157. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_comments.py +0 -0
  158. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_commit_message.py +0 -0
  159. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_compaction_pause_handler.py +0 -0
  160. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_crash_recovery.py +0 -0
  161. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_criteria_oracle.py +0 -0
  162. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_cycle_loop.py +0 -0
  163. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_cycle_loop_multicycle.py +0 -0
  164. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_cycle_loop_phase6_5.py +0 -0
  165. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_cycle_loop_phase8.py +0 -0
  166. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_cycle_ollama_model.py +0 -0
  167. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_cycle_prompts.py +0 -0
  168. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_delta_planning.py +0 -0
  169. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_dependencies.py +0 -0
  170. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_detect.py +0 -0
  171. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_deviation_detector.py +0 -0
  172. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_dispatch_graph_report.py +0 -0
  173. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_docs_no_default_max_turns.py +0 -0
  174. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_docs_ollama_model_guide.py +0 -0
  175. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_docs_ollama_pro.py +0 -0
  176. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_effective_model_routing.py +0 -0
  177. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_effort.py +0 -0
  178. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_effort_routing_consistency.py +0 -0
  179. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_env.py +0 -0
  180. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_failure_report.py +0 -0
  181. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_gemini_preflight.py +0 -0
  182. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_gemini_runner.py +0 -0
  183. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_generate.py +0 -0
  184. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_generate_ollama.py +0 -0
  185. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_generate_resume.py +0 -0
  186. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_gh.py +0 -0
  187. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_gh_actions.py +0 -0
  188. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_gh_issues_create.py +0 -0
  189. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_gh_labels.py +0 -0
  190. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_gh_milestones.py +0 -0
  191. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_gh_repo_threading.py +0 -0
  192. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_gh_submodules.py +0 -0
  193. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_git_ops.py +0 -0
  194. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_init.py +0 -0
  195. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_logging_setup.py +0 -0
  196. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_manifest.py +0 -0
  197. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_max_turns_cli_flag.py +0 -0
  198. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_mcp.py +0 -0
  199. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_memory.py +0 -0
  200. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_memory_tool.py +0 -0
  201. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_memory_writers.py +0 -0
  202. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_message_parsing.py +0 -0
  203. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_no_max_turns_in_call_sites.py +0 -0
  204. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_no_max_turns_literal.py +0 -0
  205. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_non_goals_grep_guard.py +0 -0
  206. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_ollama_budget.py +0 -0
  207. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_ollama_hardening.py +0 -0
  208. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_ollama_rate_limit.py +0 -0
  209. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_optimize.py +0 -0
  210. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_options.py +0 -0
  211. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase0.py +0 -0
  212. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase1.py +0 -0
  213. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase2.py +0 -0
  214. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase2_cache_regression.py +0 -0
  215. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase2_multicycle_token_reduction.py +0 -0
  216. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase2_token_reduction.py +0 -0
  217. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase3_4.py +0 -0
  218. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase5.py +0 -0
  219. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase5_precommit.py +0 -0
  220. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase6.py +0 -0
  221. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase65_budget.py +0 -0
  222. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase6_5_integration.py +0 -0
  223. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase6_5_integration_cycle2.py +0 -0
  224. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase6_5_port_isolation.py +0 -0
  225. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase6_5_prompt.py +0 -0
  226. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase6_5_prompt_snapshot.py +0 -0
  227. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase6_5_run.py +0 -0
  228. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase6_5_state.py +0 -0
  229. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase6_5_tiers.py +0 -0
  230. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_access_control.py +0 -0
  231. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_changelog.py +0 -0
  232. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_ci_classifier.py +0 -0
  233. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_local_verify.py +0 -0
  234. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_prompt_snapshots.py +0 -0
  235. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_prompts.py +0 -0
  236. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_repair_loop.py +0 -0
  237. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_repair_sota.py +0 -0
  238. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_semver.py +0 -0
  239. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_state.py +0 -0
  240. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_status.py +0 -0
  241. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_surface_scanner.py +0 -0
  242. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_trigger.py +0 -0
  243. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase_mcp_regression.py +0 -0
  244. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase_telemetry.py +0 -0
  245. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase_token_logging.py +0 -0
  246. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_preflight.py +0 -0
  247. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_preflight_ollama.py +0 -0
  248. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_prompt_drift.py +0 -0
  249. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_prompt_prefix_snapshots.py +0 -0
  250. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_prompt_prefix_stability.py +0 -0
  251. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_prompts.py +0 -0
  252. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_rate_limit.py +0 -0
  253. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_repo_info.py +0 -0
  254. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_requirements_structure.py +0 -0
  255. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_resume.py +0 -0
  256. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_retry.py +0 -0
  257. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_review.py +0 -0
  258. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_runner_protocol.py +0 -0
  259. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_runner_protocol_annotations.py +0 -0
  260. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_runner_types.py +0 -0
  261. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_runner_utils.py +0 -0
  262. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_scenario_builder.py +0 -0
  263. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_sdk_runner.py +0 -0
  264. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_sdk_runner_shared.py +0 -0
  265. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_session_mode.py +0 -0
  266. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_sica.py +0 -0
  267. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_state.py +0 -0
  268. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_state_guard.py +0 -0
  269. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_state_retention.py +0 -0
  270. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_status.py +0 -0
  271. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_subprocess_runner.py +0 -0
  272. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_telemetry.py +0 -0
  273. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_tier_selector.py +0 -0
  274. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_tier_t3.py +0 -0
  275. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_tier_t3_pass_rate.py +0 -0
  276. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_version.py +0 -0
  277. {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/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.3
3
+ Version: 0.6.4
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.3"
7
+ version = "0.6.4"
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.3
3
+ Version: 0.6.4
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
@@ -210,6 +210,7 @@ tests/test_phase5.py
210
210
  tests/test_phase5_precommit.py
211
211
  tests/test_phase6.py
212
212
  tests/test_phase65_budget.py
213
+ tests/test_phase6_5_endpoint_source.py
213
214
  tests/test_phase6_5_integration.py
214
215
  tests/test_phase6_5_integration_cycle2.py
215
216
  tests/test_phase6_5_port_isolation.py
@@ -217,6 +218,7 @@ tests/test_phase6_5_prompt.py
217
218
  tests/test_phase6_5_prompt_snapshot.py
218
219
  tests/test_phase6_5_run.py
219
220
  tests/test_phase6_5_state.py
221
+ tests/test_phase6_5_t4_timeout.py
220
222
  tests/test_phase6_5_tiers.py
221
223
  tests/test_phase7.py
222
224
  tests/test_phase8_access_control.py
@@ -268,5 +270,6 @@ tests/test_tier_selector.py
268
270
  tests/test_tier_t3.py
269
271
  tests/test_tier_t3_pass_rate.py
270
272
  tests/test_tier_t4.py
273
+ tests/test_tier_t4_port_discovery.py
271
274
  tests/test_version.py
272
275
  tests/test_vigil.py
@@ -91,6 +91,35 @@ def git_current_branch(cwd: Path) -> str:
91
91
  return result.stdout.strip()
92
92
 
93
93
 
94
+ def git_default_branch(cwd: Path) -> str:
95
+ """Return the remote's default branch name (best effort).
96
+
97
+ Resolves ``refs/remotes/origin/HEAD`` (set by ``git clone`` /
98
+ ``git remote set-head``). Falls back to ``"main"`` when the symbolic
99
+ ref is absent — e.g. a freshly ``git init``-ed repo with no remote,
100
+ or a sentinel ``.git`` directory in tests. This function never
101
+ raises: callers use it only to decide whether the current branch is
102
+ the integration branch, and a wrong guess degrades to the safe
103
+ (status-quo) path.
104
+
105
+ Args:
106
+ cwd: Directory to run git in.
107
+
108
+ Returns:
109
+ Default branch name, or ``"main"`` when it cannot be determined.
110
+ """
111
+ result = _run(
112
+ ["git", "symbolic-ref", "--quiet", "--short", "refs/remotes/origin/HEAD"],
113
+ cwd=cwd,
114
+ check=False,
115
+ )
116
+ ref = result.stdout.strip()
117
+ if result.returncode == 0 and ref:
118
+ # ``origin/main`` → ``main``
119
+ return ref.split("/", 1)[1] if ref.startswith("origin/") else ref
120
+ return "main"
121
+
122
+
94
123
  def git_has_staged(cwd: Path) -> bool:
95
124
  """Return ``True`` when the git index has staged changes.
96
125
 
@@ -96,6 +96,17 @@ _POLL_INTERVAL_S = 0.5
96
96
  # emit, so ``_is_timeout`` can re-classify without false positives.
97
97
  _TIMEOUT_RETURNCODE = 124
98
98
 
99
+ # T4 (container lifecycle) needs its own budget, NOT the per-probe
100
+ # ``TierConfig.timeout_s`` (5.0s) that suits the fast T0/T1/T2 HTTP checks.
101
+ # ``docker compose up -d --build`` of a real stack (image build + a
102
+ # postgres healthcheck with ``interval: 10s`` and an api ``start-period:
103
+ # 40s``) cannot finish in 5s — binding command_timeout_s to 5.0 made T4
104
+ # fail by construction on every docker-shipping project even when the
105
+ # container lifecycle was reproducibly green (~45s warm). These bound the
106
+ # docker subprocess and the health wait respectively.
107
+ _T4_COMMAND_TIMEOUT_S = 600.0 # cold `compose up --build` can take minutes
108
+ _T4_BOOT_TIMEOUT_S = 180.0 # must exceed api start-period + db healthcheck
109
+
99
110
 
100
111
  class VerificationFailed(RuntimeError):
101
112
  """Raised when Phase 6.5 exhausts its repair budget with red tiers."""
@@ -700,14 +711,29 @@ async def run(
700
711
  # stable across the retry loop; re-checked per attempt by the guard (#2).
701
712
  verify_port = _alloc_free_port()
702
713
 
703
- for attempt in range(max_retries):
714
+ # Loop runs max_retries+1 verifications and at most max_retries repair
715
+ # passes. The trailing verification re-probes the app *after* the final
716
+ # repair so an agent fix on the last attempt is actually counted —
717
+ # without it the loop discarded a green post-repair state and raised
718
+ # VerificationFailed against the pre-repair red state (0.6.4 incident).
719
+ for attempt in range(max_retries + 1):
704
720
  budget.check(turns_consumed=attempt, logger=logger)
705
721
  progress = _begin_attempt(target, progress, attempt)
706
722
  save_state(state_path, state)
707
723
  if attempt > 0:
708
724
  _soft_reset_for_repair(project_dir, attempt + 1)
709
725
 
710
- verify_port = _ensure_verify_port(verify_port, logger=logger)
726
+ # Pre-flight squatter guard (#2) applies ONLY before the first
727
+ # attempt — at that point no repair-pass server exists, so an open
728
+ # port is a genuine foreign listener and rerolling protects the
729
+ # retry budget. On attempts >=1 the port is *expected* to be open:
730
+ # the repair agent boots the app and deliberately leaves it alive
731
+ # so the orchestrator's authoritative T2 re-probe hits it. Rerolling
732
+ # then would move the port out from under the agent's live server
733
+ # every attempt and guarantee a T2 miss (the actual 0.6.3 failure).
734
+ # The ephemeral port (#1) already eliminates the real collision.
735
+ if attempt == 0:
736
+ verify_port = _ensure_verify_port(verify_port, logger=logger)
711
737
  outcome = _run_tiers(
712
738
  project_dir,
713
739
  state,
@@ -729,6 +755,9 @@ async def run(
729
755
  event = detector.check()
730
756
  if event is not None:
731
757
  raise VerificationFailed(f"Phase 6.5 deviation: {event.detail}")
758
+ if attempt == max_retries:
759
+ # Repair budget exhausted; the trailing verify already ran above.
760
+ break
732
761
  await _run_repair_pass(
733
762
  failure_report=_format_failure_report(outcome, attempt + 1, max_retries),
734
763
  shared_client=shared_client,
@@ -805,13 +834,37 @@ def _run_tiers(
805
834
  _record_skips(target, progress, spec, attempt, logger=logger)
806
835
  save_state(state_path, state)
807
836
 
837
+ # When T1 actually skipped at runtime (no in-process listener) AND T4
838
+ # is enabled (docker present), T2's in-process probe is structurally
839
+ # doomed: no service binds the ephemeral verify port. Probing anyway
840
+ # yields an identical ConnectError every attempt and
841
+ # DeviationDetector.stuck_tier trips before the repair agent can stand
842
+ # up the container. T4 already replays the T2 contract against the
843
+ # published container port, so T2 is genuinely redundant on this
844
+ # codepath — skip it at runtime.
845
+ t4_enabled = any(t.tier_id == "T4" and t.enabled for t in spec.tiers)
846
+ t1_skipped_at_runtime = False
847
+
808
848
  for tier in spec.tiers:
809
849
  if not tier.enabled:
810
850
  continue
851
+ if tier.tier_id == "T2" and t1_skipped_at_runtime and t4_enabled:
852
+ _record_runtime_skip(
853
+ target,
854
+ progress,
855
+ tier_id="T2",
856
+ reason="T1 skipped (no in-process entry_cmd); T4 covers verification",
857
+ attempt=attempt,
858
+ logger=logger,
859
+ )
860
+ save_state(state_path, state)
861
+ continue
811
862
  runner = _tier_runner(tier, project_dir, env, base_url, endpoints)
812
863
  if runner is None:
813
864
  continue
814
865
  result = runner()
866
+ if tier.tier_id == "T1" and result.skipped:
867
+ t1_skipped_at_runtime = True
815
868
  _record_outcome(target, progress, tier.tier_id, result, attempt)
816
869
  if detector is not None and not result.skipped:
817
870
  detector.record(
@@ -850,6 +903,35 @@ def _persist_spec_for_attempt(project_dir: Path, requirements_path: Path, port:
850
903
  return spec
851
904
 
852
905
 
906
+ def _record_runtime_skip(
907
+ target: Any,
908
+ progress: Phase65Progress,
909
+ *,
910
+ tier_id: str,
911
+ reason: str,
912
+ attempt: int,
913
+ logger: logging.Logger,
914
+ ) -> None:
915
+ """Mark *tier_id* as skipped at runtime and surface the reason on progress.
916
+
917
+ Mirrors :func:`_record_skips` for spec-declared skips, but for tiers
918
+ whose skip is decided dynamically (e.g. T2 when T1 has no entry_cmd).
919
+ """
920
+ if tier_id not in progress.tiers_skipped:
921
+ progress.tiers_skipped.append(tier_id)
922
+ progress.skip_reasons[tier_id] = reason
923
+ logger.warning("Phase 6.5: tier %s skipped at runtime — %s", tier_id, reason)
924
+ updated = Phase65Progress(
925
+ attempt=attempt,
926
+ last_tier=progress.last_tier,
927
+ tiers_passed=list(progress.tiers_passed),
928
+ tiers_failed=list(progress.tiers_failed),
929
+ tiers_skipped=list(progress.tiers_skipped),
930
+ skip_reasons=dict(progress.skip_reasons),
931
+ )
932
+ target.phase6_5 = updated
933
+
934
+
853
935
  def _record_skips(
854
936
  target: Any,
855
937
  progress: Phase65Progress,
@@ -914,15 +996,50 @@ def _tier_runner(
914
996
  env=env,
915
997
  base_url=base_url,
916
998
  endpoints=endpoints,
917
- command_timeout_s=timeout_s,
999
+ command_timeout_s=_T4_COMMAND_TIMEOUT_S,
1000
+ boot_timeout_s=_T4_BOOT_TIMEOUT_S,
918
1001
  )
919
1002
  return None
920
1003
 
921
1004
 
922
1005
  def _load_endpoints(requirements_path: Path) -> list[Endpoint]:
1006
+ """T2 endpoint set, sourced from the Oracle's T2-classified acceptance
1007
+ criteria — NOT a whole-file scrape.
1008
+
1009
+ Whole-file ``extract_endpoints_from_requirements`` scrapes every
1010
+ backticked ``/token`` from every ``- [ ]`` checkbox, including
1011
+ implementation-task bullets. On the optimizer that promoted the
1012
+ Angular routing line ``- [ ] Update routing to `/portfolio-builder` …``
1013
+ to a backend GET probe, which 404s by design (it is a frontend page on
1014
+ :4300, not a FastAPI route) and exhausted the Phase 6.5 retry budget on
1015
+ a verifier false positive while the app was healthy.
1016
+
1017
+ The Oracle already scopes endpoint detection to the acceptance-criteria
1018
+ sections and is the contract the prompt orders the *agent* to trust
1019
+ ("do not re-classify by hand"); the orchestrator's programmatic T2 must
1020
+ trust the same source. Zero T2 criteria → empty list → T2 is
1021
+ health-only. Lazy import mirrors :func:`_load_oracle_report_text`
1022
+ (criteria_oracle imports back into this module).
1023
+ """
923
1024
  if not requirements_path.exists():
924
1025
  return []
925
- return extract_endpoints_from_requirements(requirements_path.read_text(encoding="utf-8"))
1026
+ from code_generator.orchestrator.criteria_oracle import build_oracle_report
1027
+
1028
+ report = build_oracle_report(requirements_path.read_text(encoding="utf-8"))
1029
+ endpoints: list[Endpoint] = []
1030
+ seen: set[tuple[str, str]] = set()
1031
+ for criterion in (*report.global_criteria, *report.scope_criteria):
1032
+ if criterion.tier != "T2":
1033
+ continue
1034
+ # Reconstruct the bullet exactly as criteria_oracle._has_endpoint
1035
+ # does, so the orchestrator and Oracle agree byte-for-byte.
1036
+ for endpoint in extract_endpoints_from_requirements(f"- [ ] {criterion.text}\n"):
1037
+ key = (endpoint.method, endpoint.path)
1038
+ if key in seen:
1039
+ continue
1040
+ seen.add(key)
1041
+ endpoints.append(endpoint)
1042
+ return endpoints
926
1043
 
927
1044
 
928
1045
  def _record_outcome(
@@ -143,18 +143,23 @@ class Phase7CommitMessageError(RuntimeError):
143
143
  def _synthesize_fallback(cycle_name: str, issues_closed: str) -> str:
144
144
  """Build a deterministic conventional-commit message from orchestrator metadata.
145
145
 
146
+ The closing footer emits one ``Closes #<n>`` line per issue — a
147
+ GitHub-recognized auto-close trailer. ``Closes #1, #2`` would only close
148
+ ``#1``; the keyword must repeat on every line.
149
+
146
150
  Args:
147
151
  cycle_name: Name of the active cycle (empty string for single mode).
148
152
  issues_closed: Comma-separated issue refs (e.g. ``"#1, #2"``) or ``"none"``.
149
153
 
150
154
  Returns:
151
- A valid conventional-commit string, optionally with a ``Closed issues`` footer.
155
+ A valid conventional-commit string, optionally with a ``Closes #<n>`` footer.
152
156
  """
153
157
  scope = f"({cycle_name})" if cycle_name else ""
154
158
  subject = f"chore{scope}: cycle complete"
155
159
  if issues_closed == "none":
156
160
  return subject
157
- return f"{subject}\n\nClosed issues: {issues_closed}"
161
+ footer = "\n".join(f"Closes {ref}" for ref in issues_closed.split(", "))
162
+ return f"{subject}\n\n{footer}"
158
163
 
159
164
 
160
165
  def _validate_commit_message(msg: str) -> None:
@@ -11,7 +11,15 @@ After the Opus 4.7 confirmation prompt returns, the orchestrator:
11
11
  3. When ``state.repo`` is ``None`` (local-only dev), records
12
12
  ``ci_status="skipped"``, ticks ``professionalized``, and returns —
13
13
  single-repo development is not penalized for a missing remote.
14
- 4. When the repo is configured, dispatches
14
+ 4. When the repo is configured but no workflow can fire for the working
15
+ branch (e.g. main-only triggers while the orchestrator works a
16
+ feature branch), the green-gate is structurally unobservable:
17
+ records ``ci_status="skipped"``, ticks ``professionalized``, and
18
+ returns — polling here would only spin until the circuit breaker
19
+ aborts an otherwise-complete run. Same philosophy as the no-remote
20
+ skip. The default branch is excluded from this guard so a genuine
21
+ misconfiguration on the integration branch still surfaces.
22
+ 5. Otherwise dispatches
15
23
  :func:`phase8_repair_loop.run_ci_green_gate`. On ``"green"``: the
16
24
  cycle-2 semver tag (if any) is relocated to the new HEAD via
17
25
  :func:`git_ops.git_tag_move`, and ``professionalized`` is ticked. On
@@ -36,6 +44,7 @@ from code_generator.git_ops import (
36
44
  git_add_all,
37
45
  git_commit,
38
46
  git_current_branch,
47
+ git_default_branch,
39
48
  git_pull_rebase,
40
49
  git_push,
41
50
  git_tag_list,
@@ -156,6 +165,41 @@ def _handle_skipped(
156
165
  _tick_professionalized_for_closed_issues(state, cycle, state_path, logger)
157
166
 
158
167
 
168
+ def _handle_unobservable(
169
+ state: State,
170
+ cycle: CycleState | None,
171
+ state_path: Path,
172
+ logger: logging.Logger,
173
+ ) -> None:
174
+ """No workflow can fire for the working branch → skip the gate cleanly."""
175
+ logger.warning(
176
+ "Phase 8: no workflow triggers on push to the working branch "
177
+ "(default branch differs); CI green-gate is structurally unobservable "
178
+ "— recording skipped instead of polling until the circuit breaker "
179
+ "aborts an otherwise-complete run."
180
+ )
181
+ _tick_professionalized_for_closed_issues(state, cycle, state_path, logger)
182
+
183
+
184
+ def _ci_green_gate_unobservable(project_dir: Path) -> bool:
185
+ """``True`` when no pushed workflow can fire for the working branch.
186
+
187
+ The green-gate polls GitHub for a workflow run on the just-pushed
188
+ branch SHA. When the repo's workflows only trigger on the default
189
+ branch (or PRs to it) and the orchestrator is working a feature
190
+ branch, *no run is ever created* — the old behaviour polled until the
191
+ circuit breaker opened and aborted an otherwise-complete pipeline.
192
+ Treat that as a structural skip (same philosophy as ``state.repo is
193
+ None``): the default branch itself is excluded so a genuine
194
+ misconfiguration on the integration branch still surfaces.
195
+ """
196
+ branch = git_current_branch(project_dir)
197
+ default = git_default_branch(project_dir)
198
+ if branch == default:
199
+ return False
200
+ return not phase8_workflow_gen.ref_has_triggerable_ci(project_dir, branch)
201
+
202
+
159
203
  async def _run_ci_green_gate(
160
204
  *,
161
205
  repo: RepoInfo,
@@ -221,22 +265,27 @@ async def run(
221
265
  _handle_skipped(state, cycle, state_path, logger)
222
266
  else:
223
267
  _commit_workflows(project_dir, logger)
224
- outcome = await _run_ci_green_gate(
225
- repo=state.repo,
226
- project_dir=project_dir,
227
- state_path=state_path,
228
- state=state,
229
- cycle=cycle,
230
- runner_module=runner_module,
231
- effective_model=effective_model,
232
- logger=logger,
233
- )
234
- target.phase8.ci_status = outcome
235
- _state.save_state(state_path, state)
236
- if outcome == "green":
237
- _handle_green(project_dir, state, cycle, state_path, logger)
268
+ if _ci_green_gate_unobservable(project_dir):
269
+ target.phase8.ci_status = "skipped"
270
+ _state.save_state(state_path, state)
271
+ _handle_unobservable(state, cycle, state_path, logger)
238
272
  else:
239
- _handle_exhausted(logger)
273
+ outcome = await _run_ci_green_gate(
274
+ repo=state.repo,
275
+ project_dir=project_dir,
276
+ state_path=state_path,
277
+ state=state,
278
+ cycle=cycle,
279
+ runner_module=runner_module,
280
+ effective_model=effective_model,
281
+ logger=logger,
282
+ )
283
+ target.phase8.ci_status = outcome
284
+ _state.save_state(state_path, state)
285
+ if outcome == "green":
286
+ _handle_green(project_dir, state, cycle, state_path, logger)
287
+ else:
288
+ _handle_exhausted(logger)
240
289
 
241
290
  target.phase8.status = "ok"
242
291
  target.phase8.finished_at = utc_now_isoformat()
@@ -97,6 +97,7 @@ name: ci
97
97
  branches: [main]
98
98
  pull_request:
99
99
  branches: [main]
100
+ workflow_dispatch: {}
100
101
  jobs:
101
102
  ci:
102
103
  runs-on: ubuntu-latest
@@ -117,6 +118,7 @@ name: ci
117
118
  branches: [main]
118
119
  pull_request:
119
120
  branches: [main]
121
+ workflow_dispatch: {}
120
122
  jobs:
121
123
  ci:
122
124
  runs-on: ubuntu-latest
@@ -137,6 +139,7 @@ name: ci
137
139
  branches: [main]
138
140
  pull_request:
139
141
  branches: [main]
142
+ workflow_dispatch: {}
140
143
  jobs:
141
144
  ci:
142
145
  runs-on: ubuntu-latest
@@ -229,6 +232,95 @@ def _build_workflow(project_dir: Path, rel_path: str, fresh_content: str) -> Wor
229
232
  return WorkflowFile(rel_path=rel_path, content=fresh_content)
230
233
 
231
234
 
235
+ # ---------------------------------------------------------------------------
236
+ # Trigger observability
237
+ # ---------------------------------------------------------------------------
238
+
239
+ _BRANCH_TOKEN = re.compile(r"[\"']?([A-Za-z0-9._/\-\*]+)[\"']?")
240
+
241
+
242
+ def _push_branches(workflow_text: str) -> tuple[list[str], bool]:
243
+ """Return ``(branch_tokens, has_unfiltered_push)`` for a workflow body.
244
+
245
+ Heuristic (not a YAML parse — consistent with
246
+ :func:`scan_for_guardrail_violations`). ``has_unfiltered_push`` is
247
+ ``True`` when a ``push`` trigger exists with no ``branches:`` filter
248
+ (such a workflow fires on every branch).
249
+ """
250
+ if not re.search(r"^\s*push\s*:", workflow_text, re.MULTILINE) and not re.search(
251
+ r"^\s*\"?on\"?\s*:\s*push\s*$", workflow_text, re.MULTILINE
252
+ ):
253
+ return [], False
254
+
255
+ tokens: list[str] = []
256
+ # Inline form: branches: [main, release/*]
257
+ for m in re.finditer(r"branches\s*:\s*\[([^\]]*)\]", workflow_text):
258
+ tokens += [t for t in _BRANCH_TOKEN.findall(m.group(1)) if t]
259
+ # Block form:
260
+ # branches:
261
+ # - main
262
+ for block in re.finditer(
263
+ r"branches\s*:\s*\n((?:\s*-\s*[^\n]+\n?)+)", workflow_text
264
+ ):
265
+ for line in block.group(1).splitlines():
266
+ mt = re.match(r"\s*-\s*[\"']?([A-Za-z0-9._/\-\*]+)[\"']?", line)
267
+ if mt:
268
+ tokens.append(mt.group(1))
269
+
270
+ has_branches_filter = "branches" in workflow_text or "tags" in workflow_text
271
+ bare_push = bool(re.search(r"^\s*\"?on\"?\s*:\s*push\s*$", workflow_text, re.MULTILINE))
272
+ unfiltered = bare_push or not has_branches_filter
273
+ return tokens, unfiltered
274
+
275
+
276
+ def _branch_matches(token: str, branch: str) -> bool:
277
+ """``True`` when a workflow branch *token* would select *branch*."""
278
+ if token in ("*", "**"):
279
+ return True
280
+ if token == branch:
281
+ return True
282
+ return token.endswith("/*") and branch.startswith(token[:-1])
283
+
284
+
285
+ def ref_has_triggerable_ci(project_dir: Path, branch: str) -> bool:
286
+ """Return ``True`` when **a push to** *branch* will produce a run.
287
+
288
+ Scans ``.github/workflows/*.y{a,}ml``. A workflow counts when it has
289
+ an unfiltered ``push`` trigger or a ``push`` branch filter that
290
+ selects *branch* (exact, ``*``/``**``, or ``prefix/*``).
291
+
292
+ ``workflow_dispatch`` deliberately does **not** count: it only makes
293
+ a workflow *manually* invocable, it does not auto-create a run when
294
+ the orchestrator pushes the branch. Phase 8's green-gate pushes then
295
+ polls for a run on that SHA, so a dispatch-only workflow is just as
296
+ unobservable as a main-only one.
297
+
298
+ Returns ``False`` when the workflows directory is absent or empty, or
299
+ when no workflow fires on a push to *branch*. Phase 8 uses this to
300
+ detect a structurally unobservable CI green-gate (e.g. main-only
301
+ triggers while the orchestrator works a feature branch) and degrade
302
+ to ``ci_status="skipped"`` instead of polling until the circuit
303
+ breaker aborts an otherwise-complete run.
304
+ """
305
+ wf_dir = project_dir / ".github" / "workflows"
306
+ if not wf_dir.is_dir():
307
+ return False
308
+ files = sorted(p for p in wf_dir.iterdir() if p.suffix in (".yml", ".yaml"))
309
+ if not files:
310
+ return False
311
+ for path in files:
312
+ try:
313
+ text = path.read_text(encoding="utf-8")
314
+ except OSError:
315
+ continue
316
+ tokens, unfiltered = _push_branches(text)
317
+ if unfiltered:
318
+ return True
319
+ if any(_branch_matches(tok, branch) for tok in tokens):
320
+ return True
321
+ return False
322
+
323
+
232
324
  # ---------------------------------------------------------------------------
233
325
  # Guardrail scanner
234
326
  # ---------------------------------------------------------------------------