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.
Files changed (114) hide show
  1. {codd_dev-1.7.0 → codd_dev-1.9.2}/.gitignore +1 -1
  2. {codd_dev-1.7.0 → codd_dev-1.9.2}/PKG-INFO +26 -8
  3. {codd_dev-1.7.0 → codd_dev-1.9.2}/README.md +25 -7
  4. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/assembler.py +49 -21
  5. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/cli.py +30 -13
  6. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/fixer.py +137 -16
  7. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/generator.py +99 -1
  8. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/implementer.py +541 -181
  9. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/planner.py +10 -0
  10. codd_dev-1.9.2/docs/requirements/README.md +17 -0
  11. {codd_dev-1.7.0 → codd_dev-1.9.2}/pyproject.toml +1 -1
  12. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/build-1.4.2.dist-info/licenses/LICENSE +0 -20
  13. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/certifi-2026.2.25.dist-info/licenses/LICENSE +0 -20
  14. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/cffi-2.0.0.dist-info/licenses/LICENSE +0 -23
  15. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/licenses/LICENSE +0 -21
  16. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/cryptography-46.0.6.dist-info/licenses/LICENSE +0 -3
  17. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/id-1.6.1.dist-info/licenses/LICENSE +0 -202
  18. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/iniconfig-2.3.0.dist-info/licenses/LICENSE +0 -21
  19. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/jaraco.classes-3.4.0.dist-info/LICENSE +0 -17
  20. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/jaraco_context-6.1.2.dist-info/licenses/LICENSE +0 -18
  21. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/jaraco_functools-4.4.0.dist-info/licenses/LICENSE +0 -18
  22. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/jeepney-0.9.0.dist-info/licenses/LICENSE +0 -21
  23. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/keyring-25.7.0.dist-info/licenses/LICENSE +0 -18
  24. 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
  25. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/mdurl-0.1.2.dist-info/LICENSE +0 -46
  26. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/more_itertools-11.0.1.dist-info/licenses/LICENSE +0 -19
  27. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/nh3-0.3.4.dist-info/licenses/LICENSE +0 -21
  28. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/packaging-26.0.dist-info/licenses/LICENSE +0 -3
  29. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip/_vendor/certifi/LICENSE +0 -20
  30. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip/_vendor/distro/LICENSE +0 -202
  31. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip/_vendor/packaging/LICENSE +0 -3
  32. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip/_vendor/pkg_resources/LICENSE +0 -17
  33. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip/_vendor/platformdirs/LICENSE +0 -21
  34. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip/_vendor/pygments/LICENSE +0 -25
  35. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/LICENSE +0 -21
  36. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip/_vendor/requests/LICENSE +0 -175
  37. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip/_vendor/resolvelib/LICENSE +0 -13
  38. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip/_vendor/rich/LICENSE +0 -19
  39. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip/_vendor/tomli/LICENSE +0 -21
  40. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip/_vendor/tomli_w/LICENSE +0 -21
  41. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pip/_vendor/truststore/LICENSE +0 -21
  42. 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
  43. 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
  44. 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
  45. 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
  46. 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
  47. 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
  48. 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
  49. 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
  50. 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
  51. 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
  52. 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
  53. 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
  54. 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
  55. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pluggy-1.6.0.dist-info/licenses/LICENSE +0 -21
  56. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pycparser-3.0.dist-info/licenses/LICENSE +0 -27
  57. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pygments-2.20.0.dist-info/licenses/LICENSE +0 -25
  58. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pyproject_hooks-1.2.0.dist-info/LICENSE +0 -21
  59. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pytest-9.0.2.dist-info/licenses/LICENSE +0 -21
  60. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/licenses/LICENSE +0 -20
  61. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/readme_renderer-44.0.dist-info/LICENSE +0 -174
  62. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/requests-2.33.1.dist-info/licenses/LICENSE +0 -175
  63. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/requests_toolbelt-1.0.0.dist-info/LICENSE +0 -13
  64. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/rfc3986-2.0.0.dist-info/LICENSE +0 -13
  65. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/rich-14.3.3.dist-info/licenses/LICENSE +0 -19
  66. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/secretstorage-3.5.0.dist-info/licenses/LICENSE +0 -25
  67. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/tree_sitter-0.25.2.dist-info/licenses/LICENSE +0 -21
  68. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/tree_sitter_java-0.23.5.dist-info/LICENSE +0 -21
  69. 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
  70. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/tree_sitter_typescript-0.23.2.dist-info/LICENSE +0 -21
  71. codd_dev-1.7.0/.venv-scanonly-209d2/lib/python3.12/site-packages/twine-6.2.0.dist-info/licenses/LICENSE +0 -174
  72. codd_dev-1.7.0/LICENSE +0 -21
  73. {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
  74. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/__init__.py +0 -0
  75. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/bridge.py +0 -0
  76. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/clustering.py +0 -0
  77. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/config.py +0 -0
  78. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/contracts.py +0 -0
  79. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/defaults.yaml +0 -0
  80. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/e2e_runner.py +0 -0
  81. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/env_refs.py +0 -0
  82. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/extract_ai.py +0 -0
  83. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/extractor.py +0 -0
  84. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/graph.py +0 -0
  85. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/hooks/__init__.py +0 -0
  86. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/hooks/pre-commit +0 -0
  87. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/inheritance.py +0 -0
  88. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/mcp_server.py +0 -0
  89. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/measure.py +0 -0
  90. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/parsing.py +0 -0
  91. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/policy.py +0 -0
  92. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/propagate.py +0 -0
  93. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/propagator.py +0 -0
  94. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/repair_slice.py +0 -0
  95. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/require.py +0 -0
  96. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/require_plugins.py +0 -0
  97. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/restore.py +0 -0
  98. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/scanner.py +0 -0
  99. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/schema_refs.py +0 -0
  100. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/synth.py +0 -0
  101. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/templates/codd.yaml.tmpl +0 -0
  102. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/templates/conventions.yaml.tmpl +0 -0
  103. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/templates/data_dependencies.yaml.tmpl +0 -0
  104. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/templates/doc_links.yaml.tmpl +0 -0
  105. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/templates/extracted/api-contract.md.j2 +0 -0
  106. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/templates/extracted/architecture-overview.md.j2 +0 -0
  107. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/templates/extracted/module-detail.md.j2 +0 -0
  108. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/templates/extracted/schema-design.md.j2 +0 -0
  109. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/templates/extracted/system-context.md.j2 +0 -0
  110. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/templates/gitignore.tmpl +0 -0
  111. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/templates/overrides.yaml.tmpl +0 -0
  112. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/traceability.py +0 -0
  113. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/validator.py +0 -0
  114. {codd_dev-1.7.0 → codd_dev-1.9.2}/codd/wiring.py +0 -0
@@ -8,7 +8,7 @@ build/
8
8
  *.egg
9
9
 
10
10
  # Virtual environments
11
- .venv/
11
+ .venv*/
12
12
  venv/
13
13
 
14
14
  # IDE
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codd-dev
3
- Version: 1.7.0
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.7.0** — `init` / `scan` / `impact` are stable. `propagate` traces code changes to downstream design docs and doc-to-doc changes via CEG graph. `extract --ai` with baseline preset. Custom `node_id` prefixes via `codd.yaml`. GitHub Action for CI integration.
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
- sprints=$(codd plan --sprints)
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.7.0** — `init` / `scan` / `impact` are stable. `propagate` traces code changes to downstream design docs and doc-to-doc changes via CEG graph. `extract --ai` with baseline preset. Custom `node_id` prefixes via `codd.yaml`. GitHub Action for CI integration.
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
- sprints=$(codd plan --sprints)
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 sprint fragments into a working project."""
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(resolved_ai_command, prompt)
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/sprint_N/."""
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
- for sprint_dir in sorted(generated_base.iterdir()):
99
- if not sprint_dir.is_dir() or not sprint_dir.name.startswith("sprint_"):
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
- for code_file in sorted(sprint_dir.rglob("*")):
102
- if code_file.is_file() and code_file.suffix in (".ts", ".tsx", ".js", ".jsx", ".py", ".go", ".java", ".css"):
103
- rel_path = code_file.relative_to(project_root)
104
- content = code_file.read_text(encoding="utf-8")
105
- fragments.append({
106
- "sprint_dir": sprint_dir.name,
107
- "path": str(rel_path),
108
- "content": content,
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 sprint.
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 sprint fragments: later sprints may refine or replace earlier ones.
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
- current_sprint = None
210
+ current_group = None
183
211
  for frag in fragments:
184
- if frag["sprint_dir"] != current_sprint:
185
- current_sprint = frag["sprint_dir"]
186
- parts.append(f"\n### {current_sprint}\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(sprint: int, path: str, task: str | None, ai_cmd: str | None):
503
- """Generate implementation code for a specific sprint."""
504
- from codd.implementer import implement_sprint
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 = implement_sprint(project_root, sprint, task=task, ai_command=ai_cmd)
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
- click.echo(f"Sprint {sprint}: {generated_files} files generated across {len(results)} task(s)")
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 to verify")
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("--sprints", is_flag=True, help="Output only the total sprint count (for shell scripting)")
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, sprints: bool, ai_cmd: str | None):
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 sprints:
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 sprints:
956
- from codd.implementer import count_sprints
957
- click.echo(count_sprints(project_root))
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 and invoke AI (fix mode: returns fixed source, writes to files)
120
- prompt = _build_fix_prompt(project_root, failures, context, config)
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, AND the current source
620
- of files mentioned in failures. The AI returns the complete fixed
621
- source for each file in fenced code blocks tagged with file paths.
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. Read the failing test/build output carefully.",
655
- "2. Use the design documents to understand the INTENDED behavior.",
656
- "3. Fix the IMPLEMENTATION code to match the design, not the other way around.",
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
- "4. Do NOT modify test files unless the test itself has a bug (e.g., wrong import path).",
665
- "5. Do NOT modify design documents.",
666
- "6. Make minimal, focused changes. Don't refactor unrelated code.",
667
- "7. Follow the target framework's lint rules and naming conventions.",
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
- "For each file you fix or create, output the COMPLETE file content in a fenced",
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