codd-dev 1.7.0__tar.gz → 1.9.2__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.
- {codd_dev-1.7.0 → codd_dev-1.9.2}/.gitignore +1 -1
- {codd_dev-1.7.0 → codd_dev-1.9.2}/PKG-INFO +26 -8
- {codd_dev-1.7.0 → codd_dev-1.9.2}/README.md +25 -7
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/assembler.py +49 -21
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/cli.py +30 -13
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/fixer.py +137 -16
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/generator.py +99 -1
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/implementer.py +541 -181
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/planner.py +10 -0
- codd_dev-1.9.2/docs/requirements/README.md +17 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/pyproject.toml +1 -1
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/build-1.4.2.dist-info/licenses/LICENSE +0 -20
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/certifi-2026.2.25.dist-info/licenses/LICENSE +0 -20
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/cffi-2.0.0.dist-info/licenses/LICENSE +0 -23
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/licenses/LICENSE +0 -21
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/cryptography-46.0.6.dist-info/licenses/LICENSE +0 -3
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/id-1.6.1.dist-info/licenses/LICENSE +0 -202
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/iniconfig-2.3.0.dist-info/licenses/LICENSE +0 -21
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/jaraco.classes-3.4.0.dist-info/LICENSE +0 -17
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/jaraco_context-6.1.2.dist-info/licenses/LICENSE +0 -18
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/jaraco_functools-4.4.0.dist-info/licenses/LICENSE +0 -18
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/jeepney-0.9.0.dist-info/licenses/LICENSE +0 -21
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/keyring-25.7.0.dist-info/licenses/LICENSE +0 -18
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/markdown_it_py-4.0.0.dist-info/licenses/LICENSE +0 -21
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/mdurl-0.1.2.dist-info/LICENSE +0 -46
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/more_itertools-11.0.1.dist-info/licenses/LICENSE +0 -19
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/nh3-0.3.4.dist-info/licenses/LICENSE +0 -21
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/packaging-26.0.dist-info/licenses/LICENSE +0 -3
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip/_vendor/certifi/LICENSE +0 -20
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip/_vendor/distro/LICENSE +0 -202
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip/_vendor/packaging/LICENSE +0 -3
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip/_vendor/pkg_resources/LICENSE +0 -17
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip/_vendor/platformdirs/LICENSE +0 -21
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip/_vendor/pygments/LICENSE +0 -25
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/LICENSE +0 -21
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip/_vendor/requests/LICENSE +0 -175
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip/_vendor/resolvelib/LICENSE +0 -13
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip/_vendor/rich/LICENSE +0 -19
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip/_vendor/tomli/LICENSE +0 -21
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip/_vendor/tomli_w/LICENSE +0 -21
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip/_vendor/truststore/LICENSE +0 -21
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/certifi/LICENSE +0 -20
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/distro/LICENSE +0 -202
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE +0 -3
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE +0 -17
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE +0 -21
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/pygments/LICENSE +0 -25
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE +0 -21
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/requests/LICENSE +0 -175
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE +0 -13
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/rich/LICENSE +0 -19
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/tomli/LICENSE +0 -21
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE +0 -21
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip-26.0.1.dist-info/licenses/src/pip/_vendor/truststore/LICENSE +0 -21
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pluggy-1.6.0.dist-info/licenses/LICENSE +0 -21
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pycparser-3.0.dist-info/licenses/LICENSE +0 -27
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pygments-2.20.0.dist-info/licenses/LICENSE +0 -25
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pyproject_hooks-1.2.0.dist-info/LICENSE +0 -21
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pytest-9.0.2.dist-info/licenses/LICENSE +0 -21
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/licenses/LICENSE +0 -20
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/readme_renderer-44.0.dist-info/LICENSE +0 -174
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/requests-2.33.1.dist-info/licenses/LICENSE +0 -175
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/requests_toolbelt-1.0.0.dist-info/LICENSE +0 -13
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/rfc3986-2.0.0.dist-info/LICENSE +0 -13
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/rich-14.3.3.dist-info/licenses/LICENSE +0 -19
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/secretstorage-3.5.0.dist-info/licenses/LICENSE +0 -25
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/tree_sitter-0.25.2.dist-info/licenses/LICENSE +0 -21
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/tree_sitter_java-0.23.5.dist-info/LICENSE +0 -21
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/tree_sitter_python-0.25.0.dist-info/licenses/LICENSE +0 -21
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/tree_sitter_typescript-0.23.2.dist-info/LICENSE +0 -21
- codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/twine-6.2.0.dist-info/licenses/LICENSE +0 -174
- codd_dev-1.7.0/LICENSE +0 -21
- {codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/codd_dev-0.2.0a5.dist-info/licenses → codd_dev-1.9.2}/LICENSE +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/__init__.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/bridge.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/clustering.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/config.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/contracts.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/defaults.yaml +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/e2e_runner.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/env_refs.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/extract_ai.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/extractor.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/graph.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/hooks/__init__.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/hooks/pre-commit +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/inheritance.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/mcp_server.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/measure.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/parsing.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/policy.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/propagate.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/propagator.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/repair_slice.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/require.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/require_plugins.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/restore.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/scanner.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/schema_refs.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/synth.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/templates/codd.yaml.tmpl +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/templates/conventions.yaml.tmpl +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/templates/data_dependencies.yaml.tmpl +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/templates/doc_links.yaml.tmpl +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/templates/extracted/api-contract.md.j2 +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/templates/extracted/architecture-overview.md.j2 +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/templates/extracted/module-detail.md.j2 +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/templates/extracted/schema-design.md.j2 +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/templates/extracted/system-context.md.j2 +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/templates/gitignore.tmpl +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/templates/overrides.yaml.tmpl +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/traceability.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/validator.py +0 -0
- {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/wiring.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codd-dev
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.9.2
|
|
4
4
|
Summary: CoDD: Coherence-Driven Development — cross-artifact change impact analysis
|
|
5
5
|
Project-URL: Homepage, https://github.com/yohey-w/codd-dev
|
|
6
6
|
Project-URL: Repository, https://github.com/yohey-w/codd-dev
|
|
@@ -49,7 +49,7 @@ Description-Content-Type: text/markdown
|
|
|
49
49
|
</p>
|
|
50
50
|
|
|
51
51
|
<p align="center">
|
|
52
|
-
<a href="README_ja.md">日本語</a> | English
|
|
52
|
+
<a href="README_ja.md">日本語</a> | English | <a href="README_zh.md">中文</a>
|
|
53
53
|
</p>
|
|
54
54
|
|
|
55
55
|
---
|
|
@@ -60,7 +60,7 @@ Description-Content-Type: text/markdown
|
|
|
60
60
|
pip install codd-dev
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
**v1.
|
|
63
|
+
**v1.9.0** — `codd implement` now supports **multi-AI engine** (Claude stdout + Codex file-writing) and **automatic parallel execution** within phases via git worktree isolation. Phase milestone format (`#### M1.1`) supported. AI command timeout extended to 1 hour for heavy reasoning models. SWE-bench Verified: **73/73 = 100%** resolved.
|
|
64
64
|
|
|
65
65
|
---
|
|
66
66
|
|
|
@@ -132,10 +132,7 @@ done
|
|
|
132
132
|
codd validate
|
|
133
133
|
|
|
134
134
|
# Generate code from design docs
|
|
135
|
-
|
|
136
|
-
for sprint in $(seq 1 $sprints); do
|
|
137
|
-
codd implement --sprint $sprint
|
|
138
|
-
done
|
|
135
|
+
codd implement
|
|
139
136
|
|
|
140
137
|
# Assemble code fragments into a buildable project
|
|
141
138
|
codd assemble
|
|
@@ -421,6 +418,19 @@ codd impact
|
|
|
421
418
|
| `codd policy` | **Alpha** | Enterprise policy checker (forbidden/required patterns in source code) |
|
|
422
419
|
| `codd measure` | **Alpha** | Project health metrics (graph, coverage, quality, health score 0-100) |
|
|
423
420
|
| `codd mcp-server` | **Alpha** | MCP server for AI tool integration (stdio, zero dependencies) |
|
|
421
|
+
| `codd fix` | **Alpha** | Auto-fix test/build failures with diagnostic reasoning and session state |
|
|
422
|
+
|
|
423
|
+
## SWE-bench Verified
|
|
424
|
+
|
|
425
|
+
CoDD's `fix` command with diagnostic reasoning achieves **73/73 = 100%** on a curated subset of [SWE-bench Verified](https://www.swebench.com/verified.html). The diagnostic step forces root cause analysis before patching, and session state prevents repeating failed approaches across retries.
|
|
426
|
+
|
|
427
|
+
| Metric | Result |
|
|
428
|
+
|--------|--------|
|
|
429
|
+
| Instances | 73 (curated from SWE-bench Verified) |
|
|
430
|
+
| Resolved | **73 (100%)** |
|
|
431
|
+
| Key feature | Diagnostic reasoning + session state persistence |
|
|
432
|
+
|
|
433
|
+
Details: [Zenn: CoDD SWE-bench Guide](https://zenn.dev/shio_shoppaize/articles/codd-swebench-pilot?locale=en)
|
|
424
434
|
|
|
425
435
|
## OSS / Pro Split
|
|
426
436
|
|
|
@@ -428,7 +438,7 @@ CoDD v1.6.0 introduced a clean OSS/Pro boundary via a bridge pattern.
|
|
|
428
438
|
|
|
429
439
|
**OSS (MIT, free)** — everything you need to keep docs coherent:
|
|
430
440
|
|
|
431
|
-
`init` · `scan` · `impact` · `generate` · `restore` · `propagate` · `extract` · `require` · `plan` · `validate` · `measure` · `policy` · `mcp-server`
|
|
441
|
+
`init` · `scan` · `impact` · `generate` · `restore` · `propagate` · `extract` · `require` · `plan` · `validate` · `measure` · `policy` · `fix` · `mcp-server`
|
|
432
442
|
|
|
433
443
|
**Pro (private, paid)** — enterprise review and verification:
|
|
434
444
|
|
|
@@ -750,6 +760,14 @@ If CoDD can't manage itself, it shouldn't manage your project.
|
|
|
750
760
|
- [Zenn: Harness as Code — A Guide to CoDD #3 Bug Fixing with CoDD extract (SWE-bench)](https://zenn.dev/shio_shoppaize/articles/codd-swebench-pilot?locale=en)
|
|
751
761
|
- [Zenn: CoDD deep-dive](https://zenn.dev/shio_shoppaize/articles/shogun-codd-coherence?locale=en)
|
|
752
762
|
|
|
763
|
+
## Sponsors
|
|
764
|
+
|
|
765
|
+
<a href="https://github.com/sponsors/yohey-w">
|
|
766
|
+
<img src="https://img.shields.io/badge/Sponsor-%E2%9D%A4-ea4aaa?style=for-the-badge&logo=github-sponsors" alt="Sponsor">
|
|
767
|
+
</a>
|
|
768
|
+
|
|
769
|
+
Your sponsorship keeps CoDD free and funds continued development. See [sponsor tiers](https://github.com/sponsors/yohey-w).
|
|
770
|
+
|
|
753
771
|
## License
|
|
754
772
|
|
|
755
773
|
MIT
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
</p>
|
|
12
12
|
|
|
13
13
|
<p align="center">
|
|
14
|
-
<a href="README_ja.md">日本語</a> | English
|
|
14
|
+
<a href="README_ja.md">日本語</a> | English | <a href="README_zh.md">中文</a>
|
|
15
15
|
</p>
|
|
16
16
|
|
|
17
17
|
---
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
pip install codd-dev
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
**v1.
|
|
25
|
+
**v1.9.0** — `codd implement` now supports **multi-AI engine** (Claude stdout + Codex file-writing) and **automatic parallel execution** within phases via git worktree isolation. Phase milestone format (`#### M1.1`) supported. AI command timeout extended to 1 hour for heavy reasoning models. SWE-bench Verified: **73/73 = 100%** resolved.
|
|
26
26
|
|
|
27
27
|
---
|
|
28
28
|
|
|
@@ -94,10 +94,7 @@ done
|
|
|
94
94
|
codd validate
|
|
95
95
|
|
|
96
96
|
# Generate code from design docs
|
|
97
|
-
|
|
98
|
-
for sprint in $(seq 1 $sprints); do
|
|
99
|
-
codd implement --sprint $sprint
|
|
100
|
-
done
|
|
97
|
+
codd implement
|
|
101
98
|
|
|
102
99
|
# Assemble code fragments into a buildable project
|
|
103
100
|
codd assemble
|
|
@@ -383,6 +380,19 @@ codd impact
|
|
|
383
380
|
| `codd policy` | **Alpha** | Enterprise policy checker (forbidden/required patterns in source code) |
|
|
384
381
|
| `codd measure` | **Alpha** | Project health metrics (graph, coverage, quality, health score 0-100) |
|
|
385
382
|
| `codd mcp-server` | **Alpha** | MCP server for AI tool integration (stdio, zero dependencies) |
|
|
383
|
+
| `codd fix` | **Alpha** | Auto-fix test/build failures with diagnostic reasoning and session state |
|
|
384
|
+
|
|
385
|
+
## SWE-bench Verified
|
|
386
|
+
|
|
387
|
+
CoDD's `fix` command with diagnostic reasoning achieves **73/73 = 100%** on a curated subset of [SWE-bench Verified](https://www.swebench.com/verified.html). The diagnostic step forces root cause analysis before patching, and session state prevents repeating failed approaches across retries.
|
|
388
|
+
|
|
389
|
+
| Metric | Result |
|
|
390
|
+
|--------|--------|
|
|
391
|
+
| Instances | 73 (curated from SWE-bench Verified) |
|
|
392
|
+
| Resolved | **73 (100%)** |
|
|
393
|
+
| Key feature | Diagnostic reasoning + session state persistence |
|
|
394
|
+
|
|
395
|
+
Details: [Zenn: CoDD SWE-bench Guide](https://zenn.dev/shio_shoppaize/articles/codd-swebench-pilot?locale=en)
|
|
386
396
|
|
|
387
397
|
## OSS / Pro Split
|
|
388
398
|
|
|
@@ -390,7 +400,7 @@ CoDD v1.6.0 introduced a clean OSS/Pro boundary via a bridge pattern.
|
|
|
390
400
|
|
|
391
401
|
**OSS (MIT, free)** — everything you need to keep docs coherent:
|
|
392
402
|
|
|
393
|
-
`init` · `scan` · `impact` · `generate` · `restore` · `propagate` · `extract` · `require` · `plan` · `validate` · `measure` · `policy` · `mcp-server`
|
|
403
|
+
`init` · `scan` · `impact` · `generate` · `restore` · `propagate` · `extract` · `require` · `plan` · `validate` · `measure` · `policy` · `fix` · `mcp-server`
|
|
394
404
|
|
|
395
405
|
**Pro (private, paid)** — enterprise review and verification:
|
|
396
406
|
|
|
@@ -712,6 +722,14 @@ If CoDD can't manage itself, it shouldn't manage your project.
|
|
|
712
722
|
- [Zenn: Harness as Code — A Guide to CoDD #3 Bug Fixing with CoDD extract (SWE-bench)](https://zenn.dev/shio_shoppaize/articles/codd-swebench-pilot?locale=en)
|
|
713
723
|
- [Zenn: CoDD deep-dive](https://zenn.dev/shio_shoppaize/articles/shogun-codd-coherence?locale=en)
|
|
714
724
|
|
|
725
|
+
## Sponsors
|
|
726
|
+
|
|
727
|
+
<a href="https://github.com/sponsors/yohey-w">
|
|
728
|
+
<img src="https://img.shields.io/badge/Sponsor-%E2%9D%A4-ea4aaa?style=for-the-badge&logo=github-sponsors" alt="Sponsor">
|
|
729
|
+
</a>
|
|
730
|
+
|
|
731
|
+
Your sponsorship keeps CoDD free and funds continued development. See [sponsor tiers](https://github.com/sponsors/yohey-w).
|
|
732
|
+
|
|
715
733
|
## License
|
|
716
734
|
|
|
717
735
|
MIT
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""CoDD assembler — integrate generated
|
|
1
|
+
"""CoDD assembler — integrate generated fragments into a working project."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
@@ -6,8 +6,11 @@ from dataclasses import dataclass
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from typing import Any
|
|
8
8
|
|
|
9
|
+
import warnings
|
|
10
|
+
|
|
9
11
|
import codd.generator as generator_module
|
|
10
12
|
from codd.generator import _load_project_config, _normalize_conventions
|
|
13
|
+
from codd.implementer import get_valid_task_slugs
|
|
11
14
|
from codd.scanner import _extract_frontmatter, build_document_node_path_map
|
|
12
15
|
|
|
13
16
|
|
|
@@ -48,7 +51,9 @@ def assemble_project(
|
|
|
48
51
|
prompt = _build_assemble_prompt(config, design_docs, fragments, dest)
|
|
49
52
|
|
|
50
53
|
# Invoke AI
|
|
51
|
-
raw_output = generator_module._invoke_ai_command(
|
|
54
|
+
raw_output = generator_module._invoke_ai_command(
|
|
55
|
+
resolved_ai_command, prompt, project_root=project_root,
|
|
56
|
+
)
|
|
52
57
|
|
|
53
58
|
# Parse and write files
|
|
54
59
|
files_written = _write_assembled_files(project_root, dest_path, raw_output)
|
|
@@ -68,7 +73,6 @@ def _collect_design_documents(project_root: Path, config: dict[str, Any]) -> lis
|
|
|
68
73
|
full_path = project_root / rel_path
|
|
69
74
|
if full_path.exists():
|
|
70
75
|
content = full_path.read_text(encoding="utf-8")
|
|
71
|
-
# Strip frontmatter for the prompt
|
|
72
76
|
stripped = _strip_frontmatter(content)
|
|
73
77
|
docs.append({
|
|
74
78
|
"node_id": node_id,
|
|
@@ -79,7 +83,11 @@ def _collect_design_documents(project_root: Path, config: dict[str, Any]) -> lis
|
|
|
79
83
|
|
|
80
84
|
|
|
81
85
|
def _collect_generated_fragments(project_root: Path, config: dict[str, Any]) -> list[dict[str, str]]:
|
|
82
|
-
"""Collect all generated code fragments from src/generated
|
|
86
|
+
"""Collect all generated code fragments from src/generated/.
|
|
87
|
+
|
|
88
|
+
Supports both flat layout (src/generated/<task>/) and legacy sprint layout
|
|
89
|
+
(src/generated/sprint_N/<task>/). Orphan directories are excluded with a warning.
|
|
90
|
+
"""
|
|
83
91
|
source_dirs = config.get("scan", {}).get("source_dirs", ["src/"])
|
|
84
92
|
generated_base = None
|
|
85
93
|
for src_dir in source_dirs:
|
|
@@ -94,19 +102,39 @@ def _collect_generated_fragments(project_root: Path, config: dict[str, Any]) ->
|
|
|
94
102
|
if not generated_base.is_dir():
|
|
95
103
|
return []
|
|
96
104
|
|
|
105
|
+
valid_slugs = get_valid_task_slugs(project_root)
|
|
106
|
+
|
|
107
|
+
code_extensions = (".ts", ".tsx", ".js", ".jsx", ".py", ".go", ".java", ".css")
|
|
97
108
|
fragments = []
|
|
98
|
-
|
|
99
|
-
|
|
109
|
+
|
|
110
|
+
orphan_dirs: set[str] = set()
|
|
111
|
+
if valid_slugs:
|
|
112
|
+
for child in generated_base.iterdir():
|
|
113
|
+
if child.is_dir() and not child.name.startswith("sprint_") and child.name not in valid_slugs:
|
|
114
|
+
orphan_dirs.add(child.name)
|
|
115
|
+
warnings.warn(
|
|
116
|
+
f"Orphan fragment directory 'generated/{child.name}' "
|
|
117
|
+
f"does not match any task in the implementation plan. Skipping.",
|
|
118
|
+
stacklevel=2,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
for code_file in sorted(generated_base.rglob("*")):
|
|
122
|
+
if not code_file.is_file() or code_file.suffix not in code_extensions:
|
|
100
123
|
continue
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
124
|
+
|
|
125
|
+
rel_to_generated = code_file.relative_to(generated_base)
|
|
126
|
+
if rel_to_generated.parts and rel_to_generated.parts[0] in orphan_dirs:
|
|
127
|
+
continue
|
|
128
|
+
|
|
129
|
+
rel_path = code_file.relative_to(project_root)
|
|
130
|
+
content = code_file.read_text(encoding="utf-8")
|
|
131
|
+
|
|
132
|
+
task_group = rel_to_generated.parts[0] if rel_to_generated.parts else "unknown"
|
|
133
|
+
fragments.append({
|
|
134
|
+
"task_group": task_group,
|
|
135
|
+
"path": str(rel_path),
|
|
136
|
+
"content": content,
|
|
137
|
+
})
|
|
110
138
|
|
|
111
139
|
return fragments
|
|
112
140
|
|
|
@@ -140,13 +168,13 @@ def _build_assemble_prompt(
|
|
|
140
168
|
## Instructions
|
|
141
169
|
|
|
142
170
|
1. Read the design documents below to understand the architecture, component tree, data model, and state management.
|
|
143
|
-
2. Read all generated code fragments — they contain implementation pieces organized by
|
|
171
|
+
2. Read all generated code fragments — they contain implementation pieces organized by task.
|
|
144
172
|
3. Produce a COMPLETE, BUILDABLE project. This includes:
|
|
145
173
|
- **Project configuration files** at the project root: package.json, tsconfig.json, next.config.*, tailwind.config.*, postcss.config.*, etc. — whatever the tech stack requires to build and run.
|
|
146
174
|
- **Entry point / scaffold files**: app/layout.tsx, app/page.tsx (for Next.js), index.html, main.py, etc. — the files that wire the application together.
|
|
147
175
|
- **Source code** under `{output_dir}/`: components, utilities, types, styles, hooks, reducers.
|
|
148
176
|
- **Style entry points**: globals.css or equivalent with framework imports (e.g. @import "tailwindcss").
|
|
149
|
-
4. Resolve conflicts between
|
|
177
|
+
4. Resolve conflicts between fragments: later tasks may refine or replace earlier ones.
|
|
150
178
|
5. Ensure all imports resolve correctly between files.
|
|
151
179
|
6. Do NOT add features beyond what the design documents specify.
|
|
152
180
|
7. Preserve traceability comments (@generated-by, @generated-from) where practical.
|
|
@@ -179,11 +207,11 @@ Do not include explanations outside of the === FILE blocks.
|
|
|
179
207
|
|
|
180
208
|
# Add generated fragments
|
|
181
209
|
parts.append("## Generated Code Fragments\n")
|
|
182
|
-
|
|
210
|
+
current_group = None
|
|
183
211
|
for frag in fragments:
|
|
184
|
-
if frag["
|
|
185
|
-
|
|
186
|
-
parts.append(f"\n### {
|
|
212
|
+
if frag["task_group"] != current_group:
|
|
213
|
+
current_group = frag["task_group"]
|
|
214
|
+
parts.append(f"\n### {current_group}\n")
|
|
187
215
|
parts.append(f"#### {frag['path']}\n```\n{frag['content']}\n```\n")
|
|
188
216
|
|
|
189
217
|
return "\n".join(parts)
|
|
@@ -491,35 +491,52 @@ def propagate(diff: str, path: str, update: bool, verify: bool, do_commit: bool,
|
|
|
491
491
|
|
|
492
492
|
|
|
493
493
|
@main.command()
|
|
494
|
-
@click.option("--sprint", required=True, type=click.IntRange(min=1), help="Sprint number to implement")
|
|
495
494
|
@click.option("--path", default=".", help="Project root directory")
|
|
496
495
|
@click.option("--task", default=None, help="Generate only one task by task ID or title match")
|
|
496
|
+
@click.option("--clean", is_flag=True, default=False, help="Remove existing generated output before re-generating")
|
|
497
497
|
@click.option(
|
|
498
498
|
"--ai-cmd",
|
|
499
499
|
default=None,
|
|
500
500
|
help="Override AI CLI command (defaults to codd.yaml ai_command or merged CoDD defaults)",
|
|
501
501
|
)
|
|
502
|
-
def implement(
|
|
503
|
-
"""Generate implementation code
|
|
504
|
-
from codd.implementer import
|
|
502
|
+
def implement(path: str, task: str | None, clean: bool, ai_cmd: str | None):
|
|
503
|
+
"""Generate implementation code from the implementation plan."""
|
|
504
|
+
from codd.implementer import implement_tasks
|
|
505
505
|
|
|
506
506
|
project_root = Path(path).resolve()
|
|
507
507
|
codd_dir = _require_codd_dir(project_root)
|
|
508
508
|
|
|
509
|
+
if clean:
|
|
510
|
+
click.echo("Cleaning src/generated/ ...")
|
|
511
|
+
|
|
509
512
|
try:
|
|
510
|
-
results =
|
|
513
|
+
results = implement_tasks(project_root, task=task, ai_command=ai_cmd, clean=clean)
|
|
511
514
|
except (FileNotFoundError, ValueError) as exc:
|
|
512
515
|
click.echo(f"Error: {exc}")
|
|
513
516
|
raise SystemExit(1)
|
|
514
517
|
|
|
515
518
|
generated_files = 0
|
|
519
|
+
failed_tasks = []
|
|
516
520
|
for result in results:
|
|
521
|
+
if result.error:
|
|
522
|
+
failed_tasks.append(result)
|
|
523
|
+
continue
|
|
517
524
|
for generated_file in result.generated_files:
|
|
518
525
|
rel_path = generated_file.relative_to(project_root)
|
|
519
526
|
click.echo(f"Generated: {rel_path} ({result.task_id})")
|
|
520
527
|
generated_files += 1
|
|
521
528
|
|
|
522
|
-
|
|
529
|
+
succeeded = len(results) - len(failed_tasks)
|
|
530
|
+
click.echo(f"{generated_files} files generated across {succeeded} task(s)")
|
|
531
|
+
|
|
532
|
+
if failed_tasks:
|
|
533
|
+
click.echo(click.style(
|
|
534
|
+
f"\nFAILED: {len(failed_tasks)} task(s) produced no files:",
|
|
535
|
+
fg="red", bold=True,
|
|
536
|
+
))
|
|
537
|
+
for ft in failed_tasks:
|
|
538
|
+
click.echo(click.style(f" ✗ {ft.task_id} ({ft.task_title}): {ft.error}", fg="red"))
|
|
539
|
+
raise SystemExit(1)
|
|
523
540
|
|
|
524
541
|
|
|
525
542
|
@main.command()
|
|
@@ -548,7 +565,7 @@ def assemble(path: str, output_dir: str | None, ai_cmd: str | None):
|
|
|
548
565
|
|
|
549
566
|
@main.command()
|
|
550
567
|
@click.option("--path", default=".", help="Project root directory")
|
|
551
|
-
@click.option("--sprint", default=None, type=click.IntRange(min=1), help="Sprint number
|
|
568
|
+
@click.option("--sprint", default=None, type=click.IntRange(min=1), help="(deprecated, ignored) Sprint number", hidden=True)
|
|
552
569
|
@click.option("--e2e", is_flag=True, default=False, help="Run E2E tests (CI-safe, excludes @cdp-only)")
|
|
553
570
|
@click.option("--deploy", is_flag=True, default=False, help="Run deploy/CDP-only E2E tests against deployed URL")
|
|
554
571
|
@click.option("--base-url", default=None, help="Override BASE_URL for E2E tests")
|
|
@@ -898,13 +915,13 @@ def policy(path: str):
|
|
|
898
915
|
@click.option("--init", "initialize", is_flag=True, help="Generate wave_config from requirement docs")
|
|
899
916
|
@click.option("--force", is_flag=True, help="Overwrite existing wave_config during --init")
|
|
900
917
|
@click.option("--waves", is_flag=True, help="Output only the total wave count (for shell scripting)")
|
|
901
|
-
@click.option("--
|
|
918
|
+
@click.option("--tasks", is_flag=True, help="Output only the total task count (for shell scripting)")
|
|
902
919
|
@click.option(
|
|
903
920
|
"--ai-cmd",
|
|
904
921
|
default=None,
|
|
905
922
|
help="Override AI CLI command for --init (defaults to codd.yaml ai_command or 'claude --print')",
|
|
906
923
|
)
|
|
907
|
-
def plan(path: str, as_json: bool, initialize: bool, force: bool, waves: bool,
|
|
924
|
+
def plan(path: str, as_json: bool, initialize: bool, force: bool, waves: bool, tasks: bool, ai_cmd: str | None):
|
|
908
925
|
"""Show wave execution status from configured artifacts."""
|
|
909
926
|
from codd.planner import build_plan, plan_init, plan_to_dict, render_plan_text
|
|
910
927
|
|
|
@@ -942,7 +959,7 @@ def plan(path: str, as_json: bool, initialize: bool, force: bool, waves: bool, s
|
|
|
942
959
|
|
|
943
960
|
if force:
|
|
944
961
|
raise click.BadOptionUsage("force", "--force requires --init")
|
|
945
|
-
if ai_cmd is not None and not waves and not
|
|
962
|
+
if ai_cmd is not None and not waves and not tasks:
|
|
946
963
|
raise click.BadOptionUsage("ai_cmd", "--ai-cmd requires --init")
|
|
947
964
|
|
|
948
965
|
if waves:
|
|
@@ -952,9 +969,9 @@ def plan(path: str, as_json: bool, initialize: bool, force: bool, waves: bool, s
|
|
|
952
969
|
click.echo(len(wave_config))
|
|
953
970
|
return
|
|
954
971
|
|
|
955
|
-
if
|
|
956
|
-
from codd.implementer import
|
|
957
|
-
click.echo(
|
|
972
|
+
if tasks:
|
|
973
|
+
from codd.implementer import get_valid_task_slugs
|
|
974
|
+
click.echo(len(get_valid_task_slugs(project_root)))
|
|
958
975
|
return
|
|
959
976
|
|
|
960
977
|
try:
|
|
@@ -38,6 +38,7 @@ class FixAttempt:
|
|
|
38
38
|
failures: list[FailureInfo]
|
|
39
39
|
fixed: bool
|
|
40
40
|
ai_output: str = ""
|
|
41
|
+
diagnosis: str = "" # root cause diagnosis from this attempt
|
|
41
42
|
|
|
42
43
|
|
|
43
44
|
@dataclass
|
|
@@ -110,27 +111,35 @@ def run_fix(
|
|
|
110
111
|
fixed=False,
|
|
111
112
|
)
|
|
112
113
|
|
|
113
|
-
# Step 2: Fix loop
|
|
114
|
+
# Step 2: Fix loop with diagnostic reasoning and session state
|
|
114
115
|
attempts: list[FixAttempt] = []
|
|
116
|
+
session_state = _SessionState()
|
|
117
|
+
|
|
115
118
|
for attempt_num in range(1, max_attempts + 1):
|
|
116
119
|
# Map failures to design context
|
|
117
120
|
context = _build_fix_context(project_root, config, failures)
|
|
118
121
|
|
|
119
|
-
# Build prompt
|
|
120
|
-
prompt = _build_fix_prompt(
|
|
122
|
+
# Build prompt with diagnosis step and session state from prior attempts
|
|
123
|
+
prompt = _build_fix_prompt(
|
|
124
|
+
project_root, failures, context, config,
|
|
125
|
+
session_state=session_state,
|
|
126
|
+
)
|
|
121
127
|
ai_output = _invoke_fix_ai(resolved_ai, prompt, project_root)
|
|
122
128
|
|
|
129
|
+
# Extract diagnosis from AI output for session state
|
|
130
|
+
diagnosis = _extract_diagnosis(ai_output)
|
|
131
|
+
|
|
123
132
|
# Re-run tests to verify
|
|
124
133
|
new_failures = _run_local_tests(project_root, config)
|
|
125
134
|
|
|
126
135
|
if new_failures is None:
|
|
127
|
-
# Tests could not run — mark as unverified, not fixed
|
|
128
136
|
logger.warning("Local tests could not run. Fix is unverified.")
|
|
129
137
|
attempts.append(FixAttempt(
|
|
130
138
|
attempt=attempt_num,
|
|
131
139
|
failures=failures,
|
|
132
140
|
fixed=False,
|
|
133
141
|
ai_output=ai_output,
|
|
142
|
+
diagnosis=diagnosis,
|
|
134
143
|
))
|
|
135
144
|
break
|
|
136
145
|
|
|
@@ -141,11 +150,21 @@ def run_fix(
|
|
|
141
150
|
failures=failures,
|
|
142
151
|
fixed=fixed,
|
|
143
152
|
ai_output=ai_output,
|
|
153
|
+
diagnosis=diagnosis,
|
|
144
154
|
))
|
|
145
155
|
|
|
146
156
|
if fixed:
|
|
147
157
|
break
|
|
148
158
|
|
|
159
|
+
# Accumulate session state for next retry
|
|
160
|
+
session_state.record_attempt(
|
|
161
|
+
attempt=attempt_num,
|
|
162
|
+
diagnosis=diagnosis,
|
|
163
|
+
failures=failures,
|
|
164
|
+
new_failures=new_failures,
|
|
165
|
+
ai_output=ai_output,
|
|
166
|
+
)
|
|
167
|
+
|
|
149
168
|
# Next iteration uses new failures
|
|
150
169
|
failures = new_failures
|
|
151
170
|
|
|
@@ -168,6 +187,84 @@ def run_fix(
|
|
|
168
187
|
)
|
|
169
188
|
|
|
170
189
|
|
|
190
|
+
# ---------------------------------------------------------------------------
|
|
191
|
+
# Session state for cross-retry diagnostic context
|
|
192
|
+
# ---------------------------------------------------------------------------
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
class _SessionState:
|
|
196
|
+
"""Accumulates diagnostic context across retry attempts.
|
|
197
|
+
|
|
198
|
+
Inspired by SWE-bench diagnose experiment (73/73 = 100%):
|
|
199
|
+
passing prior attempt history — what was tried, what failed, and why —
|
|
200
|
+
dramatically reduces wasted retries.
|
|
201
|
+
"""
|
|
202
|
+
|
|
203
|
+
def __init__(self) -> None:
|
|
204
|
+
self.prior_attempts: list[dict[str, str]] = []
|
|
205
|
+
|
|
206
|
+
def record_attempt(
|
|
207
|
+
self,
|
|
208
|
+
attempt: int,
|
|
209
|
+
diagnosis: str,
|
|
210
|
+
failures: list[FailureInfo],
|
|
211
|
+
new_failures: list[FailureInfo],
|
|
212
|
+
ai_output: str,
|
|
213
|
+
) -> None:
|
|
214
|
+
summary = {
|
|
215
|
+
"attempt": str(attempt),
|
|
216
|
+
"diagnosis": diagnosis[:500],
|
|
217
|
+
"original_errors": "; ".join(f.summary for f in failures)[:300],
|
|
218
|
+
"result_after_fix": (
|
|
219
|
+
"all tests passed" if not new_failures
|
|
220
|
+
else "; ".join(f.summary for f in new_failures)[:300]
|
|
221
|
+
),
|
|
222
|
+
"approach_summary": _summarize_approach(ai_output)[:500],
|
|
223
|
+
}
|
|
224
|
+
self.prior_attempts.append(summary)
|
|
225
|
+
|
|
226
|
+
def format_for_prompt(self) -> str:
|
|
227
|
+
if not self.prior_attempts:
|
|
228
|
+
return ""
|
|
229
|
+
lines = ["## Prior attempts (DO NOT repeat these — try a different approach)\n"]
|
|
230
|
+
for pa in self.prior_attempts:
|
|
231
|
+
lines.append(f"### Attempt {pa['attempt']}")
|
|
232
|
+
lines.append(f"- Diagnosis: {pa['diagnosis']}")
|
|
233
|
+
lines.append(f"- Approach: {pa['approach_summary']}")
|
|
234
|
+
lines.append(f"- Result: {pa['result_after_fix']}")
|
|
235
|
+
lines.append("")
|
|
236
|
+
return "\n".join(lines)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _summarize_approach(ai_output: str) -> str:
|
|
240
|
+
"""Extract a brief summary of what the AI changed from its output."""
|
|
241
|
+
# Look for explanation text after the last code block
|
|
242
|
+
parts = ai_output.rsplit("```", 1)
|
|
243
|
+
if len(parts) > 1:
|
|
244
|
+
explanation = parts[1].strip()
|
|
245
|
+
if explanation:
|
|
246
|
+
return explanation[:500]
|
|
247
|
+
# Fallback: first 200 chars
|
|
248
|
+
return ai_output[:200]
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def _extract_diagnosis(ai_output: str) -> str:
|
|
252
|
+
"""Extract the diagnosis section from AI output."""
|
|
253
|
+
# Look for ## Diagnosis or ## Root Cause sections
|
|
254
|
+
for marker in ("## Diagnosis", "## Root Cause", "**Diagnosis:**", "**Root Cause:**"):
|
|
255
|
+
idx = ai_output.find(marker)
|
|
256
|
+
if idx >= 0:
|
|
257
|
+
# Extract until next ## or code block
|
|
258
|
+
rest = ai_output[idx + len(marker):]
|
|
259
|
+
end = len(rest)
|
|
260
|
+
for stop in ("\n## ", "\n```"):
|
|
261
|
+
pos = rest.find(stop)
|
|
262
|
+
if pos >= 0 and pos < end:
|
|
263
|
+
end = pos
|
|
264
|
+
return rest[:end].strip()[:500]
|
|
265
|
+
return ""
|
|
266
|
+
|
|
267
|
+
|
|
171
268
|
# ---------------------------------------------------------------------------
|
|
172
269
|
# AI invocation for fix (source-in → fixed-source-out → write back)
|
|
173
270
|
# ---------------------------------------------------------------------------
|
|
@@ -613,12 +710,14 @@ def _build_fix_prompt(
|
|
|
613
710
|
failures: list[FailureInfo],
|
|
614
711
|
design_context: str,
|
|
615
712
|
config: dict[str, Any],
|
|
713
|
+
*,
|
|
714
|
+
session_state: _SessionState | None = None,
|
|
616
715
|
) -> str:
|
|
617
716
|
"""Build the prompt for AI to fix failures.
|
|
618
717
|
|
|
619
|
-
The prompt includes: error logs, design docs,
|
|
620
|
-
|
|
621
|
-
|
|
718
|
+
The prompt includes: error logs, design docs, current source of files
|
|
719
|
+
mentioned in failures, and (on retries) session state from prior attempts.
|
|
720
|
+
Requires the AI to diagnose root cause BEFORE writing any fix.
|
|
622
721
|
"""
|
|
623
722
|
project_name = config.get("project", {}).get("name", project_root.name)
|
|
624
723
|
language = config.get("project", {}).get("language", "unknown")
|
|
@@ -635,9 +734,21 @@ def _build_fix_prompt(
|
|
|
635
734
|
# Collect current source of files mentioned in failures
|
|
636
735
|
source_section = _collect_source_files(project_root, failures)
|
|
637
736
|
|
|
737
|
+
# Session state from prior attempts (if retrying)
|
|
738
|
+
session_section = ""
|
|
739
|
+
if session_state and session_state.prior_attempts:
|
|
740
|
+
session_section = session_state.format_for_prompt()
|
|
741
|
+
|
|
638
742
|
lines = [
|
|
639
743
|
f"You are fixing failures in the project '{project_name}' ({language}).",
|
|
640
744
|
"",
|
|
745
|
+
]
|
|
746
|
+
|
|
747
|
+
# Insert session state before failures (so AI sees what NOT to repeat)
|
|
748
|
+
if session_section:
|
|
749
|
+
lines.extend([session_section, ""])
|
|
750
|
+
|
|
751
|
+
lines.extend([
|
|
641
752
|
"## Failures to fix",
|
|
642
753
|
"",
|
|
643
754
|
*failure_section,
|
|
@@ -651,9 +762,16 @@ def _build_fix_prompt(
|
|
|
651
762
|
"",
|
|
652
763
|
"## Instructions",
|
|
653
764
|
"",
|
|
654
|
-
"1
|
|
655
|
-
"
|
|
656
|
-
"
|
|
765
|
+
"### Step 1: Diagnose (MANDATORY — do this BEFORE writing any fix)",
|
|
766
|
+
"",
|
|
767
|
+
"Write a `## Diagnosis` section that answers:",
|
|
768
|
+
"1. What is the root cause of each failure?",
|
|
769
|
+
"2. Which file(s) and line(s) are responsible?",
|
|
770
|
+
"3. What is the correct behavior according to the design docs?",
|
|
771
|
+
"",
|
|
772
|
+
"### Step 2: Fix",
|
|
773
|
+
"",
|
|
774
|
+
"1. Fix the IMPLEMENTATION code to match the design, not the other way around.",
|
|
657
775
|
" - If tests fail, fix the source code so tests pass.",
|
|
658
776
|
" - If a test expects an endpoint/method/feature that doesn't exist in code,",
|
|
659
777
|
" ADD the missing implementation as described in the design documents.",
|
|
@@ -661,15 +779,18 @@ def _build_fix_prompt(
|
|
|
661
779
|
" - If build fails (type errors, import errors), fix the source code.",
|
|
662
780
|
" - If lint fails, fix the lint issues in the source code.",
|
|
663
781
|
" - If a tool prompted interactively in CI (missing config), create the required config file.",
|
|
664
|
-
"
|
|
665
|
-
"
|
|
666
|
-
"
|
|
667
|
-
"
|
|
782
|
+
"2. Do NOT modify test files unless the test itself has a bug (e.g., wrong import path).",
|
|
783
|
+
"3. Do NOT modify design documents.",
|
|
784
|
+
"4. Make minimal, focused changes. Don't refactor unrelated code.",
|
|
785
|
+
"5. Follow the target framework's lint rules and naming conventions.",
|
|
668
786
|
" Avoid using global/reserved names (module, exports, require, etc.) as local variables.",
|
|
669
787
|
"",
|
|
670
788
|
"## Output format (CRITICAL)",
|
|
671
789
|
"",
|
|
672
|
-
"
|
|
790
|
+
"## Diagnosis",
|
|
791
|
+
"(your root cause analysis here)",
|
|
792
|
+
"",
|
|
793
|
+
"Then for each file you fix or create, output the COMPLETE file content in a fenced",
|
|
673
794
|
"code block tagged with the language and the file path (relative to project root):",
|
|
674
795
|
"",
|
|
675
796
|
"```<language> <relative/path/to/file>",
|
|
@@ -683,7 +804,7 @@ def _build_fix_prompt(
|
|
|
683
804
|
"```",
|
|
684
805
|
"",
|
|
685
806
|
"After all code blocks, briefly explain what you fixed and why.",
|
|
686
|
-
]
|
|
807
|
+
])
|
|
687
808
|
|
|
688
809
|
return "\n".join(lines)
|
|
689
810
|
|