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.
- {claude_code_generator-0.6.3/src/claude_code_generator.egg-info → claude_code_generator-0.6.4}/PKG-INFO +1 -1
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/pyproject.toml +1 -1
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4/src/claude_code_generator.egg-info}/PKG-INFO +1 -1
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/claude_code_generator.egg-info/SOURCES.txt +3 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/git_ops.py +29 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase6_5_verify.py +121 -4
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase7_commit.py +7 -2
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase8_finalization.py +65 -16
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase8_workflow_gen.py +92 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/tier_t4.py +159 -32
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/prompt-phase-3-implementation.md +1 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/prompt-phase-6-5-verify.md +2 -1
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/prompt-phase-6-test.md +1 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/prompt-phase-7-commit.md +14 -5
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_git_ops_phase8.py +29 -0
- claude_code_generator-0.6.4/tests/test_phase6_5_endpoint_source.py +64 -0
- claude_code_generator-0.6.4/tests/test_phase6_5_t4_timeout.py +68 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase7.py +6 -6
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_finalization.py +48 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_workflow_gen.py +74 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_tier_t4.py +53 -1
- claude_code_generator-0.6.4/tests/test_tier_t4_port_discovery.py +90 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/LICENSE +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/README.md +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/setup.cfg +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/claude_code_generator.egg-info/dependency_links.txt +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/claude_code_generator.egg-info/entry_points.txt +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/claude_code_generator.egg-info/requires.txt +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/claude_code_generator.egg-info/top_level.txt +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/__init__.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/agents.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/archive_run.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/changelog.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/checklist.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/cli.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/__init__.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/_bench_io.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/_crash_recovery.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/_detect.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/_dispatch.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/_resume.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/_validators.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/bench.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/bench_compare.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/bench_export.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/generate.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/init.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/optimize.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/review.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/commands/status.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/effort.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/env.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/exceptions.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/gh/__init__.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/gh/actions.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/gh/core.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/gh/issues.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/gh/labels.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/gh/milestones.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/logging_setup.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/manifest.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/memory.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/__init__.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/_cache_warmup.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/_client_lifecycle.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/_comments.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/_memory_writers.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/_phase5_precommit.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/agent_spec.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/criteria_oracle.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/cycle_loop.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/cycle_prompts.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/deviation_detector.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/failure_report.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/ollama_budget.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase0_complexity.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase1_plan.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase2_review.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase3_4_implement.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase5_closure.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase65_budget.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase6_test.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase8_ci_classifier.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase8_local_verify.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/phase8_repair_loop.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/scenario_builder.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/sica.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/tier_selector.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/tier_t3.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/orchestrator/vigil.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/preflight.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/__init__.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/hashes.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/prompt-cycle-specializer.md +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/prompt-optimize-requirements.md +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/prompt-phase-0-complexity.md +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/prompt-phase-1-planning.md +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/prompt-phase-2-batch-review.md +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/prompt-phase-5-final-review.md +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/prompt-phase-8-finalization.md +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/prompt-phase-8-repair.md +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/prompts/prompt-review.md +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/repo_info.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/requirements_structure.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/__init__.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/_telemetry.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/batch.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/cache_breakpoints.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/codex_runner.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/compaction_pause.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/fake_runner.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/gemini_runner.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/mcp.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/memory_tool.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/message_parsing.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/options.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/phase_telemetry.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/protocol.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/rate_limit.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/retry.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/sdk_runner.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/soft_reset.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/state_guard.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/subprocess_runner.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/types.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/runner/utils.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/semver.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/state.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/state_retention.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/surface_scanner.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/templates/__init__.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/templates/angular.md +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/templates/base.md +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/templates/fastapi.md +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/templates/finance.md +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/templates/fullstack.md +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/templates/nestjs.md +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/src/code_generator/templates/python-cli.md +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_agent_spec.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_agents.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_archive_run.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_bench.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_bench_compare.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_bench_export.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_bench_fixture.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_bench_regression.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_cache_breakpoints.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_cache_ttl_ordering.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_cache_warmup.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_changelog.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_checklist.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_claude_md.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_cli_io_logging.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_client_lifecycle.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_codex_preflight.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_codex_runner.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_comments.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_commit_message.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_compaction_pause_handler.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_crash_recovery.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_criteria_oracle.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_cycle_loop.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_cycle_loop_multicycle.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_cycle_loop_phase6_5.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_cycle_loop_phase8.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_cycle_ollama_model.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_cycle_prompts.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_delta_planning.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_dependencies.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_detect.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_deviation_detector.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_dispatch_graph_report.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_docs_no_default_max_turns.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_docs_ollama_model_guide.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_docs_ollama_pro.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_effective_model_routing.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_effort.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_effort_routing_consistency.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_env.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_failure_report.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_gemini_preflight.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_gemini_runner.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_generate.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_generate_ollama.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_generate_resume.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_gh.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_gh_actions.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_gh_issues_create.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_gh_labels.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_gh_milestones.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_gh_repo_threading.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_gh_submodules.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_git_ops.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_init.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_logging_setup.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_manifest.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_max_turns_cli_flag.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_mcp.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_memory.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_memory_tool.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_memory_writers.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_message_parsing.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_no_max_turns_in_call_sites.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_no_max_turns_literal.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_non_goals_grep_guard.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_ollama_budget.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_ollama_hardening.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_ollama_rate_limit.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_optimize.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_options.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase0.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase1.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase2.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase2_cache_regression.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase2_multicycle_token_reduction.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase2_token_reduction.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase3_4.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase5.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase5_precommit.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase6.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase65_budget.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase6_5_integration.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase6_5_integration_cycle2.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase6_5_port_isolation.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase6_5_prompt.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase6_5_prompt_snapshot.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase6_5_run.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase6_5_state.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase6_5_tiers.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_access_control.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_changelog.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_ci_classifier.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_local_verify.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_prompt_snapshots.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_prompts.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_repair_loop.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_repair_sota.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_semver.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_state.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_status.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_surface_scanner.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase8_trigger.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase_mcp_regression.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase_telemetry.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_phase_token_logging.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_preflight.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_preflight_ollama.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_prompt_drift.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_prompt_prefix_snapshots.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_prompt_prefix_stability.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_prompts.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_rate_limit.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_repo_info.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_requirements_structure.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_resume.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_retry.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_review.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_runner_protocol.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_runner_protocol_annotations.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_runner_types.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_runner_utils.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_scenario_builder.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_sdk_runner.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_sdk_runner_shared.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_session_mode.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_sica.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_state.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_state_guard.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_state_retention.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_status.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_subprocess_runner.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_telemetry.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_tier_selector.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_tier_t3.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_tier_t3_pass_rate.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_version.py +0 -0
- {claude_code_generator-0.6.3 → claude_code_generator-0.6.4}/tests/test_vigil.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "claude-code-generator"
|
|
7
|
-
version = "0.6.
|
|
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" }
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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=
|
|
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
|
-
|
|
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 ``
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
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
|
-
|
|
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
|
# ---------------------------------------------------------------------------
|