codeplain 0.3.2__tar.gz → 0.3.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (231) hide show
  1. {codeplain-0.3.2 → codeplain-0.3.3}/PKG-INFO +8 -2
  2. {codeplain-0.3.2 → codeplain-0.3.3}/README.md +5 -0
  3. codeplain-0.3.3/_version.py +1 -0
  4. {codeplain-0.3.2 → codeplain-0.3.3}/cli_output/status.py +14 -9
  5. {codeplain-0.3.2 → codeplain-0.3.3}/examples/example_hello_world_python/hello_world_python.plain +3 -3
  6. {codeplain-0.3.2 → codeplain-0.3.3}/plain2code.py +28 -23
  7. {codeplain-0.3.2 → codeplain-0.3.3}/plain2code_state.py +4 -0
  8. codeplain-0.3.3/plain2code_telemetry.py +114 -0
  9. {codeplain-0.3.2 → codeplain-0.3.3}/pyproject.toml +2 -1
  10. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/code_renderer.py +2 -0
  11. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/render_context.py +23 -44
  12. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/state_machine_config.py +22 -63
  13. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/triggers.py +1 -1
  14. {codeplain-0.3.2 → codeplain-0.3.3}/requirements.txt +1 -0
  15. {codeplain-0.3.2 → codeplain-0.3.3}/tests/test_cli_output.py +119 -13
  16. codeplain-0.3.3/tests/test_telemetry.py +204 -0
  17. codeplain-0.3.2/.claude/settings.local.json +0 -66
  18. codeplain-0.3.2/.zed/settings.json +0 -46
  19. codeplain-0.3.2/_version.py +0 -1
  20. codeplain-0.3.2/examples/example_hello_world_python/text.txt +0 -1
  21. {codeplain-0.3.2 → codeplain-0.3.3}/.claude/settings.json +0 -0
  22. {codeplain-0.3.2 → codeplain-0.3.3}/.flake8 +0 -0
  23. {codeplain-0.3.2 → codeplain-0.3.3}/.github/dependabot.yml +0 -0
  24. {codeplain-0.3.2 → codeplain-0.3.3}/.github/workflows/e2e.yml +0 -0
  25. {codeplain-0.3.2 → codeplain-0.3.3}/.github/workflows/lint-and-test.yml +0 -0
  26. {codeplain-0.3.2 → codeplain-0.3.3}/.github/workflows/nofity-slack-on-main-merge.yml +0 -0
  27. {codeplain-0.3.2 → codeplain-0.3.3}/.github/workflows/publish-install-script.yml +0 -0
  28. {codeplain-0.3.2 → codeplain-0.3.3}/.github/workflows/publish-to-pypi.yml +0 -0
  29. {codeplain-0.3.2 → codeplain-0.3.3}/.gitignore +0 -0
  30. {codeplain-0.3.2 → codeplain-0.3.3}/.vscode/launch.json +0 -0
  31. {codeplain-0.3.2 → codeplain-0.3.3}/.vscode/settings.json +0 -0
  32. {codeplain-0.3.2 → codeplain-0.3.3}/CLAUDE.md +0 -0
  33. {codeplain-0.3.2 → codeplain-0.3.3}/LICENSE +0 -0
  34. {codeplain-0.3.2 → codeplain-0.3.3}/cli_output/__init__.py +0 -0
  35. {codeplain-0.3.2 → codeplain-0.3.3}/cli_output/dry_run.py +0 -0
  36. {codeplain-0.3.2 → codeplain-0.3.3}/cli_output/render_summary.py +0 -0
  37. {codeplain-0.3.2 → codeplain-0.3.3}/codeplain_REST_api.py +0 -0
  38. {codeplain-0.3.2 → codeplain-0.3.3}/concept_utils.py +0 -0
  39. {codeplain-0.3.2 → codeplain-0.3.3}/config/__init__.py +0 -0
  40. {codeplain-0.3.2 → codeplain-0.3.3}/config/system_config.yaml +0 -0
  41. {codeplain-0.3.2 → codeplain-0.3.3}/diff_utils.py +0 -0
  42. {codeplain-0.3.2 → codeplain-0.3.3}/docs/generate_cli.py +0 -0
  43. {codeplain-0.3.2 → codeplain-0.3.3}/docs/plain2code_cli.md +0 -0
  44. {codeplain-0.3.2 → codeplain-0.3.3}/docs/starting_a_plain_project_from_scratch.md +0 -0
  45. {codeplain-0.3.2 → codeplain-0.3.3}/event_bus.py +0 -0
  46. {codeplain-0.3.2 → codeplain-0.3.3}/examples/example_hello_world_golang/config.yaml +0 -0
  47. {codeplain-0.3.2 → codeplain-0.3.3}/examples/example_hello_world_golang/harness_tests/hello_world_test.go +0 -0
  48. {codeplain-0.3.2 → codeplain-0.3.3}/examples/example_hello_world_golang/hello_world_golang.plain +0 -0
  49. {codeplain-0.3.2 → codeplain-0.3.3}/examples/example_hello_world_golang/run.sh +0 -0
  50. {codeplain-0.3.2 → codeplain-0.3.3}/examples/example_hello_world_python/config.yaml +0 -0
  51. {codeplain-0.3.2 → codeplain-0.3.3}/examples/example_hello_world_python/harness_tests/hello_world_display/test_hello_world.py +0 -0
  52. {codeplain-0.3.2 → codeplain-0.3.3}/examples/example_hello_world_python/run.sh +0 -0
  53. {codeplain-0.3.2 → codeplain-0.3.3}/examples/example_hello_world_react/config.yaml +0 -0
  54. {codeplain-0.3.2 → codeplain-0.3.3}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress/e2e/hello_world.cy.ts +0 -0
  55. {codeplain-0.3.2 → codeplain-0.3.3}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress/support/e2e.ts +0 -0
  56. {codeplain-0.3.2 → codeplain-0.3.3}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress.config.ts +0 -0
  57. {codeplain-0.3.2 → codeplain-0.3.3}/examples/example_hello_world_react/harness_tests/hello_world_display/package.json +0 -0
  58. {codeplain-0.3.2 → codeplain-0.3.3}/examples/example_hello_world_react/harness_tests/hello_world_display/tsconfig.json +0 -0
  59. {codeplain-0.3.2 → codeplain-0.3.3}/examples/example_hello_world_react/hello_world_react.plain +0 -0
  60. {codeplain-0.3.2 → codeplain-0.3.3}/examples/example_hello_world_react/run.sh +0 -0
  61. {codeplain-0.3.2 → codeplain-0.3.3}/examples/run.sh +0 -0
  62. {codeplain-0.3.2 → codeplain-0.3.3}/file_utils.py +0 -0
  63. {codeplain-0.3.2 → codeplain-0.3.3}/git_utils.py +0 -0
  64. {codeplain-0.3.2 → codeplain-0.3.3}/install/bash/examples.sh +0 -0
  65. {codeplain-0.3.2 → codeplain-0.3.3}/install/bash/install.sh +0 -0
  66. {codeplain-0.3.2 → codeplain-0.3.3}/install/bash/walkthrough.sh +0 -0
  67. {codeplain-0.3.2 → codeplain-0.3.3}/install/powershell/examples.ps1 +0 -0
  68. {codeplain-0.3.2 → codeplain-0.3.3}/install/powershell/install.ps1 +0 -0
  69. {codeplain-0.3.2 → codeplain-0.3.3}/install/powershell/walkthrough.ps1 +0 -0
  70. {codeplain-0.3.2 → codeplain-0.3.3}/memory_management.py +0 -0
  71. {codeplain-0.3.2 → codeplain-0.3.3}/module_renderer.py +0 -0
  72. {codeplain-0.3.2 → codeplain-0.3.3}/partial_rendering.py +0 -0
  73. {codeplain-0.3.2 → codeplain-0.3.3}/plain2code_arguments.py +0 -0
  74. {codeplain-0.3.2 → codeplain-0.3.3}/plain2code_console.py +0 -0
  75. {codeplain-0.3.2 → codeplain-0.3.3}/plain2code_events.py +0 -0
  76. {codeplain-0.3.2 → codeplain-0.3.3}/plain2code_exceptions.py +0 -0
  77. {codeplain-0.3.2 → codeplain-0.3.3}/plain2code_logger.py +0 -0
  78. {codeplain-0.3.2 → codeplain-0.3.3}/plain2code_nodes.py +0 -0
  79. {codeplain-0.3.2 → codeplain-0.3.3}/plain2code_read_config.py +0 -0
  80. {codeplain-0.3.2 → codeplain-0.3.3}/plain2code_utils.py +0 -0
  81. {codeplain-0.3.2 → codeplain-0.3.3}/plain_file.py +0 -0
  82. {codeplain-0.3.2 → codeplain-0.3.3}/plain_modules.py +0 -0
  83. {codeplain-0.3.2 → codeplain-0.3.3}/plain_spec.py +0 -0
  84. {codeplain-0.3.2 → codeplain-0.3.3}/pytest.ini +0 -0
  85. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/__init__.py +0 -0
  86. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/actions/analyze_specification_ambiguity.py +0 -0
  87. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/actions/base_action.py +0 -0
  88. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/actions/commit_conformance_tests_changes.py +0 -0
  89. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/actions/commit_implementation_code_changes.py +0 -0
  90. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/actions/create_dist.py +0 -0
  91. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/actions/exit_with_error.py +0 -0
  92. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/actions/finish_functional_requirement.py +0 -0
  93. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/actions/fix_conformance_test.py +0 -0
  94. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/actions/fix_unit_tests.py +0 -0
  95. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/actions/prepare_repositories.py +0 -0
  96. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/actions/prepare_testing_environment.py +0 -0
  97. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/actions/refactor_code.py +0 -0
  98. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/actions/render_conformance_tests.py +0 -0
  99. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/actions/render_functional_requirement.py +0 -0
  100. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/actions/run_conformance_tests.py +0 -0
  101. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/actions/run_unit_tests.py +0 -0
  102. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/actions/summarize_conformance_tests.py +0 -0
  103. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/conformance_tests.py +0 -0
  104. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/implementation_code_helpers.py +0 -0
  105. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/render_types.py +0 -0
  106. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/render_utils.py +0 -0
  107. {codeplain-0.3.2 → codeplain-0.3.3}/render_machine/states.py +0 -0
  108. {codeplain-0.3.2 → codeplain-0.3.3}/resources/codeplain_overview.png +0 -0
  109. {codeplain-0.3.2 → codeplain-0.3.3}/resources/plain_example.png +0 -0
  110. {codeplain-0.3.2 → codeplain-0.3.3}/standard_template_library/__init__.py +0 -0
  111. {codeplain-0.3.2 → codeplain-0.3.3}/standard_template_library/golang-console-app-template.plain +0 -0
  112. {codeplain-0.3.2 → codeplain-0.3.3}/standard_template_library/python-console-app-template.plain +0 -0
  113. {codeplain-0.3.2 → codeplain-0.3.3}/standard_template_library/typescript-react-app-boilerplate.plain +0 -0
  114. {codeplain-0.3.2 → codeplain-0.3.3}/standard_template_library/typescript-react-app-template.plain +0 -0
  115. {codeplain-0.3.2 → codeplain-0.3.3}/system_config.py +0 -0
  116. {codeplain-0.3.2 → codeplain-0.3.3}/test_scripts/run_conformance_tests_cypress.ps1 +0 -0
  117. {codeplain-0.3.2 → codeplain-0.3.3}/test_scripts/run_conformance_tests_cypress.sh +0 -0
  118. {codeplain-0.3.2 → codeplain-0.3.3}/test_scripts/run_conformance_tests_golang.ps1 +0 -0
  119. {codeplain-0.3.2 → codeplain-0.3.3}/test_scripts/run_conformance_tests_golang.sh +0 -0
  120. {codeplain-0.3.2 → codeplain-0.3.3}/test_scripts/run_conformance_tests_python.ps1 +0 -0
  121. {codeplain-0.3.2 → codeplain-0.3.3}/test_scripts/run_conformance_tests_python.sh +0 -0
  122. {codeplain-0.3.2 → codeplain-0.3.3}/test_scripts/run_unittests_flutter.ps1 +0 -0
  123. {codeplain-0.3.2 → codeplain-0.3.3}/test_scripts/run_unittests_golang.ps1 +0 -0
  124. {codeplain-0.3.2 → codeplain-0.3.3}/test_scripts/run_unittests_golang.sh +0 -0
  125. {codeplain-0.3.2 → codeplain-0.3.3}/test_scripts/run_unittests_python.ps1 +0 -0
  126. {codeplain-0.3.2 → codeplain-0.3.3}/test_scripts/run_unittests_python.sh +0 -0
  127. {codeplain-0.3.2 → codeplain-0.3.3}/test_scripts/run_unittests_react.ps1 +0 -0
  128. {codeplain-0.3.2 → codeplain-0.3.3}/test_scripts/run_unittests_react.sh +0 -0
  129. {codeplain-0.3.2 → codeplain-0.3.3}/tests/__init__.py +0 -0
  130. {codeplain-0.3.2 → codeplain-0.3.3}/tests/conftest.py +0 -0
  131. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/imports/circular_imports_1.plain +0 -0
  132. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/imports/circular_imports_2.plain +0 -0
  133. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/imports/circular_imports_main.plain +0 -0
  134. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/imports/diamond_import_1.plain +0 -0
  135. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/imports/diamond_import_2.plain +0 -0
  136. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/imports/diamond_import_common.plain +0 -0
  137. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/imports/diamond_imports_main.plain +0 -0
  138. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/imports/non_existent_import.plain +0 -0
  139. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/partial_rendering/pr_leaf.plain +0 -0
  140. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/partial_rendering/pr_middle.plain +0 -0
  141. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/partial_rendering/pr_root.plain +0 -0
  142. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/partial_rendering/pr_solo.plain +0 -0
  143. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfile/add_new_task_modal_specification.yaml +0 -0
  144. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfile/duplicate_specification_heading.plain +0 -0
  145. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfile/invalid_specification_order.plain +0 -0
  146. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfile/missing_non_functional_requirements.plain +0 -0
  147. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfile/plain_source_with_absolute_link.plain +0 -0
  148. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfile/plain_source_with_url_link.plain +0 -0
  149. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfile/task_list_ui_specification.yaml +0 -0
  150. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfile/task_manager_with_reference_links.plain +0 -0
  151. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfile/without_non_functional_requirement.plain +0 -0
  152. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/concept_validation_acceptance_tests.plain +0 -0
  153. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/concept_validation_acceptance_tests_nondefined.plain +0 -0
  154. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/concept_validation_defined_nondefined.plain +0 -0
  155. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/concept_validation_defined_nondefined_2.plain +0 -0
  156. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/concept_validation_definition.plain +0 -0
  157. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/concept_validation_noconcepts.plain +0 -0
  158. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/concept_validation_nonconcept.plain +0 -0
  159. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/concept_validation_nondefined.plain +0 -0
  160. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/concept_validation_redefinition.plain +0 -0
  161. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/concept_validation_several_concepts.plain +0 -0
  162. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/concept_validation_valid.plain +0 -0
  163. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/exported_concepts.plain +0 -0
  164. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/exported_concepts_base.plain +0 -0
  165. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/exported_concepts_example.plain +0 -0
  166. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/exported_concepts_missing.plain +0 -0
  167. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/exported_concepts_missing_example.plain +0 -0
  168. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/exported_concepts_nested.plain +0 -0
  169. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/exported_concepts_nested_example.plain +0 -0
  170. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/exported_concepts_transitive_example.plain +0 -0
  171. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/exported_concepts_transitive_l1.plain +0 -0
  172. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/exported_concepts_transitive_l2.plain +0 -0
  173. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/plain_file_parser_with_comments.plain +0 -0
  174. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/plain_file_with_comments_indented.plain +0 -0
  175. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/regular_plain_source.plain +0 -0
  176. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/required_concepts.plain +0 -0
  177. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/required_concepts_defs.plain +0 -0
  178. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/required_concepts_example.plain +0 -0
  179. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/required_concepts_l1.plain +0 -0
  180. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/required_concepts_l2.plain +0 -0
  181. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/required_concepts_missing.plain +0 -0
  182. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/required_concepts_module.plain +0 -0
  183. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/required_concepts_partial.plain +0 -0
  184. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/required_concepts_partial_duplicate.plain +0 -0
  185. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/topological_sort.plain +0 -0
  186. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/plainfileparser/topological_sort_not_referenced.plain +0 -0
  187. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/requires/circular_requires_main.plain +0 -0
  188. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/requires/circular_requires_sub.plain +0 -0
  189. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/requires/diamond_requires_1.plain +0 -0
  190. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/requires/diamond_requires_2.plain +0 -0
  191. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/requires/diamond_requires_common.plain +0 -0
  192. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/requires/diamond_requires_main.plain +0 -0
  193. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/requires/independent_requires_1.plain +0 -0
  194. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/requires/independent_requires_2.plain +0 -0
  195. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/requires/independent_requires_main.plain +0 -0
  196. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/requires/non_existent_require.plain +0 -0
  197. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/requires/normal_requires_1.plain +0 -0
  198. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/requires/normal_requires_2.plain +0 -0
  199. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/requires/normal_requires_common.plain +0 -0
  200. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/requires/normal_requires_main.plain +0 -0
  201. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/simple.plain +0 -0
  202. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/templates/block_level_include.plain +0 -0
  203. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/templates/code_variables.plain +0 -0
  204. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/templates/header.plain +0 -0
  205. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/templates/implement.plain +0 -0
  206. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/templates/implement_2.plain +0 -0
  207. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/templates/template_include.plain +0 -0
  208. {codeplain-0.3.2 → codeplain-0.3.3}/tests/data/templates/test_hardest_problem.plain +0 -0
  209. {codeplain-0.3.2 → codeplain-0.3.3}/tests/e2e/Dockerfile +0 -0
  210. {codeplain-0.3.2 → codeplain-0.3.3}/tests/e2e/conftest.py +0 -0
  211. {codeplain-0.3.2 → codeplain-0.3.3}/tests/e2e/test_hello_world_python.py +0 -0
  212. {codeplain-0.3.2 → codeplain-0.3.3}/tests/e2e/test_hello_world_python_windows.py +0 -0
  213. {codeplain-0.3.2 → codeplain-0.3.3}/tests/test_file_utils.py +0 -0
  214. {codeplain-0.3.2 → codeplain-0.3.3}/tests/test_git_utils.py +0 -0
  215. {codeplain-0.3.2 → codeplain-0.3.3}/tests/test_imports.py +0 -0
  216. {codeplain-0.3.2 → codeplain-0.3.3}/tests/test_partial_rendering.py +0 -0
  217. {codeplain-0.3.2 → codeplain-0.3.3}/tests/test_plain_modules.py +0 -0
  218. {codeplain-0.3.2 → codeplain-0.3.3}/tests/test_plainfile.py +0 -0
  219. {codeplain-0.3.2 → codeplain-0.3.3}/tests/test_plainfileparser.py +0 -0
  220. {codeplain-0.3.2 → codeplain-0.3.3}/tests/test_plainspec.py +0 -0
  221. {codeplain-0.3.2 → codeplain-0.3.3}/tests/test_requires.py +0 -0
  222. {codeplain-0.3.2 → codeplain-0.3.3}/tests/test_resolve_config_file.py +0 -0
  223. {codeplain-0.3.2 → codeplain-0.3.3}/tui/__init__.py +0 -0
  224. {codeplain-0.3.2 → codeplain-0.3.3}/tui/components.py +0 -0
  225. {codeplain-0.3.2 → codeplain-0.3.3}/tui/models.py +0 -0
  226. {codeplain-0.3.2 → codeplain-0.3.3}/tui/plain2code_tui.py +0 -0
  227. {codeplain-0.3.2 → codeplain-0.3.3}/tui/plain_module_render_choice_tui.py +0 -0
  228. {codeplain-0.3.2 → codeplain-0.3.3}/tui/spinner.py +0 -0
  229. {codeplain-0.3.2 → codeplain-0.3.3}/tui/state_handlers.py +0 -0
  230. {codeplain-0.3.2 → codeplain-0.3.3}/tui/styles.css +0 -0
  231. {codeplain-0.3.2 → codeplain-0.3.3}/tui/widget_helpers.py +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeplain
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: Transform plain language specifications into working code
5
5
  License-File: LICENSE
6
6
  Classifier: Environment :: Console
7
7
  Classifier: Intended Audience :: Developers
8
8
  Classifier: Operating System :: OS Independent
9
9
  Classifier: Topic :: Software Development :: Code Generators
10
- Requires-Python: >=3.11
10
+ Requires-Python: ==3.11
11
11
  Requires-Dist: gitpython==3.1.50
12
12
  Requires-Dist: mistletoe==1.3.0
13
13
  Requires-Dist: networkx==3.6.1
@@ -16,6 +16,7 @@ Requires-Dist: python-liquid2==0.3.0
16
16
  Requires-Dist: pyyaml==6.0.3
17
17
  Requires-Dist: requests==2.34.0
18
18
  Requires-Dist: rich==15.0.0
19
+ Requires-Dist: sentry-sdk==2.62.0
19
20
  Requires-Dist: textual>=7.5.0
20
21
  Requires-Dist: tiktoken==0.12.0
21
22
  Requires-Dist: transitions==0.9.3
@@ -103,6 +104,11 @@ After completing the installation steps above, you can immediately test the syst
103
104
  python hello_world.py
104
105
  ```
105
106
 
107
+ ### Crash reporting
108
+
109
+ If the plain2code client crashes unexpectedly, it sends an anonymous crash report to Codeplain to help improve the tool. To disable crash reporting, set the `CODEPLAIN_NO_TELEMETRY=1` flag.
110
+
111
+
106
112
  ## Releasing
107
113
 
108
114
  Releases are built and published with [uv](https://docs.astral.sh/uv/). The version is read from `_version.py`.
@@ -74,6 +74,11 @@ After completing the installation steps above, you can immediately test the syst
74
74
  python hello_world.py
75
75
  ```
76
76
 
77
+ ### Crash reporting
78
+
79
+ If the plain2code client crashes unexpectedly, it sends an anonymous crash report to Codeplain to help improve the tool. To disable crash reporting, set the `CODEPLAIN_NO_TELEMETRY=1` flag.
80
+
81
+
77
82
  ## Releasing
78
83
 
79
84
  Releases are built and published with [uv](https://docs.astral.sh/uv/). The version is read from `_version.py`.
@@ -0,0 +1 @@
1
+ __version__ = "0.3.3"
@@ -50,8 +50,8 @@ def _display_credit_line(plan_credits: dict) -> None:
50
50
  console.print(f" {plan_label:10} {bar} {remaining:2} of {total} remaining expires {formatted_date}")
51
51
 
52
52
 
53
- def _display_purchased_credit_line(bucket: dict) -> None:
54
- """Display a purchased credit line with progress bar."""
53
+ def _display_bucket_credit_line(bucket: dict, label: str) -> None:
54
+ """Display a credit bucket line (purchased or promo) with progress bar."""
55
55
  remaining = bucket["remaining"]
56
56
  total = bucket["total"]
57
57
  expiry_date = bucket["expiry_date"]
@@ -71,12 +71,12 @@ def _display_purchased_credit_line(bucket: dict) -> None:
71
71
  bar = _create_progress_bar(remaining, total, width=30)
72
72
 
73
73
  if is_expired:
74
- console.print(f" Purchased {bar} expired {formatted_date}")
74
+ console.print(f" {label:10} {bar} expired {formatted_date}")
75
75
  else:
76
- console.print(f" Purchased {bar} {remaining:2} of {total} remaining expires {formatted_date}")
76
+ console.print(f" {label:10} {bar} {remaining:2} of {total} remaining expires {formatted_date}")
77
77
 
78
78
 
79
- def _display_status_message(plan_credits: Optional[dict], purchased_credits: list) -> None:
79
+ def _display_status_message(plan_credits: Optional[dict], purchased_credits: list, promo_credits: list) -> None:
80
80
  """Display appropriate status message based on credit state."""
81
81
  has_remaining = False
82
82
 
@@ -93,8 +93,8 @@ def _display_status_message(plan_credits: Optional[dict], purchased_credits: lis
93
93
  if remaining > 0 and period_end > now:
94
94
  has_remaining = True
95
95
 
96
- # Check if any purchased credits have remaining balance and not expired
97
- for bucket in purchased_credits:
96
+ # Check if any purchased or promo credits have remaining balance and not expired
97
+ for bucket in [*purchased_credits, *promo_credits]:
98
98
  dt_str = bucket["expiry_date"].replace("Z", "+00:00")
99
99
  expiry_date = datetime.fromisoformat(dt_str)
100
100
  # If naive datetime, assume UTC
@@ -127,6 +127,7 @@ def print_status(api_key: str, api_url: str, client_version: str) -> None:
127
127
  org_owner = response.get("organization_owner_email")
128
128
  plan_credits = response.get("plan_credits")
129
129
  purchased_credits = response.get("purchased_credits", [])
130
+ promo_credits = response.get("promo_credits", [])
130
131
 
131
132
  # Display header information
132
133
  if client_version_valid:
@@ -153,8 +154,12 @@ def print_status(api_key: str, api_url: str, client_version: str) -> None:
153
154
 
154
155
  # Display purchased credits
155
156
  for bucket in purchased_credits:
156
- _display_purchased_credit_line(bucket)
157
+ _display_bucket_credit_line(bucket, "Purchased")
158
+
159
+ # Display promo credits
160
+ for bucket in promo_credits:
161
+ _display_bucket_credit_line(bucket, "Promo")
157
162
 
158
163
  # Display status messages and management link
159
- _display_status_message(plan_credits, purchased_credits)
164
+ _display_status_message(plan_credits, purchased_credits, promo_credits)
160
165
  console.print("\nTo manage your plan navigate to https://platform.codeplain.ai/plans")
@@ -13,7 +13,7 @@ import:
13
13
  - Display "hello, world"
14
14
 
15
15
  ***acceptance tests***
16
+
17
+ - :App: should exit with status code 0 indicating successful execution.
16
18
 
17
- - :App: should exit with status code 0 indicating successful execution [adsa](asdsa).
18
-
19
- - :App: should complete execution in under 1 second.
19
+ - :App: should complete execution in under 1 second.
@@ -49,6 +49,7 @@ from plain2code_logger import (
49
49
  get_log_file_path,
50
50
  )
51
51
  from plain2code_state import RunState
52
+ from plain2code_telemetry import capture_crash, initialize_telemetry
52
53
  from system_config import system_config
53
54
  from tui.plain2code_tui import Plain2CodeTUI
54
55
  from tui.plain_module_render_choice_tui import PlainModuleRenderChoiceTUI
@@ -56,6 +57,27 @@ from tui.plain_module_render_choice_tui import PlainModuleRenderChoiceTUI
56
57
  DEFAULT_TEMPLATE_DIRS = importlib.resources.files("standard_template_library")
57
58
  RENDER_THREAD_SHUTDOWN_TIMEOUT = 0.7
58
59
 
60
+ # Exceptions that represent expected, user-facing error conditions. They are
61
+ # reported to the user directly and must never be sent to Sentry as crashes.
62
+ EXPECTED_EXCEPTIONS = (
63
+ InvalidFridArgument,
64
+ FileNotFoundError,
65
+ MissingResource,
66
+ TemplateNotFoundError,
67
+ PlainSyntaxError,
68
+ MissingPreviousFunctionalitiesError,
69
+ MissingAPIKey,
70
+ InvalidAPIKey,
71
+ OutdatedClientVersion,
72
+ ConflictingRequirements,
73
+ RenderingCreditBalanceTooLow,
74
+ NetworkConnectionError,
75
+ ModuleDoesNotExistError,
76
+ UnsupportedResourceType,
77
+ GitNotInstalledError,
78
+ SystemExit,
79
+ )
80
+
59
81
 
60
82
  def setup_logging(
61
83
  args,
@@ -324,6 +346,8 @@ def main(): # noqa: C901
324
346
  args, event_bus, run_state, args.log_to_file, args.log_file_name, args.filename, args.headless
325
347
  )
326
348
 
349
+ initialize_telemetry()
350
+
327
351
  exc_info = None
328
352
  error_message = None
329
353
 
@@ -340,36 +364,17 @@ def main(): # noqa: C901
340
364
  else:
341
365
  error_message = str(e) if str(e) else repr(e)
342
366
 
343
- if not isinstance(
344
- e,
345
- (
346
- InvalidFridArgument,
347
- FileNotFoundError,
348
- MissingResource,
349
- TemplateNotFoundError,
350
- PlainSyntaxError,
351
- MissingPreviousFunctionalitiesError,
352
- MissingAPIKey,
353
- InvalidAPIKey,
354
- OutdatedClientVersion,
355
- ConflictingRequirements,
356
- RenderingCreditBalanceTooLow,
357
- NetworkConnectionError,
358
- ModuleDoesNotExistError,
359
- UnsupportedResourceType,
360
- GitNotInstalledError,
361
- ),
362
- ):
367
+ if not isinstance(e, EXPECTED_EXCEPTIONS):
363
368
  exc_info = sys.exc_info()
364
369
  finally:
370
+ if exc_info:
371
+ dump_crash_logs(args, run_state)
372
+ capture_crash(exc_info, run_state, args)
365
373
  print_exit_summary(
366
374
  run_state,
367
375
  args.filename,
368
376
  error_message=error_message,
369
377
  )
370
- if exc_info:
371
- # Log traceback
372
- dump_crash_logs(args, run_state)
373
378
 
374
379
  if args.headless and (exc_info is not None or not run_state.render_succeeded):
375
380
  sys.exit(1)
@@ -23,6 +23,10 @@ class RunState:
23
23
  self.unittest_batch_id: int = 0
24
24
  self.render_time_accumulated: int = 0
25
25
  self.last_render_start_timestamp: float = time.monotonic()
26
+ # Mirrored from the render state machine for crash reporting; not authoritative.
27
+ self.current_module: Optional[str] = None
28
+ self.current_frid: Optional[str] = None
29
+ self.current_render_state: Optional[str] = None
26
30
 
27
31
  def increment_call_count(self):
28
32
  self.call_count += 1
@@ -0,0 +1,114 @@
1
+ """Crash reporting via Sentry.
2
+
3
+ Only unexpected exceptions are reported (the caller decides which exceptions are
4
+ expected; see EXPECTED_EXCEPTIONS in plain2code.py). Reporting is on by default
5
+ and can be disabled by setting the CODEPLAIN_NO_TELEMETRY environment variable
6
+ to any non-empty value.
7
+ """
8
+
9
+ import os
10
+ from typing import Any, Optional
11
+
12
+ import sentry_sdk
13
+ from sentry_sdk.integrations.atexit import AtexitIntegration
14
+ from sentry_sdk.integrations.dedupe import DedupeIntegration
15
+ from sentry_sdk.integrations.modules import ModulesIntegration
16
+ from sentry_sdk.scrubber import DEFAULT_DENYLIST, EventScrubber
17
+
18
+ from plain2code_state import RunState
19
+ from system_config import system_config
20
+
21
+ SENTRY_DSN = "https://64d0d86b50b34e2dede3e4eaf5142282@o4510793955934208.ingest.us.sentry.io/4511540621213696"
22
+
23
+ NO_TELEMETRY_ENV_VAR = "CODEPLAIN_NO_TELEMETRY"
24
+ ENVIRONMENT_ENV_VAR = "CODEPLAIN_ENV"
25
+ DEFAULT_ENVIRONMENT = "production"
26
+
27
+ FLUSH_TIMEOUT_SECONDS = 2
28
+
29
+ # Local variable names whose values may contain proprietary spec or generated
30
+ # code content and must be scrubbed from stack traces (extends Sentry's
31
+ # default denylist, which already covers api_key, auth, secrets etc.).
32
+ # "headers" and "x-api-key" cover the request headers local in
33
+ # codeplain_REST_api.post_request; the default denylist only has the
34
+ # underscore form "x_api_key".
35
+ SCRUB_DENYLIST = DEFAULT_DENYLIST + [
36
+ "authorization",
37
+ "headers",
38
+ "x-api-key",
39
+ "plain_source",
40
+ "plain_source_tree",
41
+ "full_plain_source",
42
+ "existing_files_content",
43
+ "file_content",
44
+ "files_content",
45
+ "content",
46
+ "source",
47
+ "response_json",
48
+ "payload",
49
+ ]
50
+
51
+
52
+ def telemetry_enabled() -> bool:
53
+ """Return True if crash reporting should be active."""
54
+ if os.environ.get(NO_TELEMETRY_ENV_VAR):
55
+ return False
56
+ return True
57
+
58
+
59
+ def initialize_telemetry(**init_overrides: Any) -> bool:
60
+ """Initialize Sentry crash reporting. Returns True if initialized."""
61
+ if not telemetry_enabled():
62
+ return False
63
+
64
+ try:
65
+ init_kwargs: dict[str, Any] = dict(
66
+ dsn=SENTRY_DSN,
67
+ release=system_config.client_version,
68
+ environment=os.environ.get(ENVIRONMENT_ENV_VAR, DEFAULT_ENVIRONMENT),
69
+ send_default_pii=False,
70
+ server_name="", # hostname is identifying; don't send it
71
+ default_integrations=False,
72
+ auto_enabling_integrations=False,
73
+ integrations=[
74
+ AtexitIntegration(callback=lambda pending, timeout: None),
75
+ DedupeIntegration(),
76
+ ModulesIntegration(),
77
+ ],
78
+ include_local_variables=True,
79
+ event_scrubber=EventScrubber(denylist=SCRUB_DENYLIST, recursive=True),
80
+ shutdown_timeout=FLUSH_TIMEOUT_SECONDS,
81
+ )
82
+ init_kwargs.update(init_overrides)
83
+ sentry_sdk.init(**init_kwargs)
84
+ return True
85
+ except Exception:
86
+ return False
87
+
88
+
89
+ def capture_crash(exc_info, run_state: Optional[RunState], args) -> bool:
90
+ """Report an unexpected crash to Sentry. Returns True if an event was sent."""
91
+ if not telemetry_enabled():
92
+ return False
93
+
94
+ try:
95
+ with sentry_sdk.new_scope() as scope:
96
+ if run_state is not None:
97
+ scope.set_tag("render_id", run_state.render_id)
98
+ scope.set_tag("render_state", run_state.current_render_state)
99
+ scope.set_tag("current_module", run_state.current_module)
100
+ scope.set_tag("current_frid", run_state.current_frid)
101
+ scope.set_tag("headless", bool(getattr(args, "headless", False)))
102
+ scope.set_tag("unittests_script_provided", bool(getattr(args, "unittests_script", None)))
103
+ scope.set_tag("conformance_tests_script_provided", bool(getattr(args, "conformance_tests_script", None)))
104
+ scope.set_tag(
105
+ "prepare_environment_script_provided", bool(getattr(args, "prepare_environment_script", None))
106
+ )
107
+
108
+ event_id = sentry_sdk.capture_exception(exc_info[1])
109
+
110
+ sentry_sdk.flush(timeout=FLUSH_TIMEOUT_SECONDS)
111
+ return event_id is not None
112
+ except Exception:
113
+ # Telemetry must never break the CLI or mask the original crash.
114
+ return False
@@ -7,7 +7,7 @@ name = "codeplain"
7
7
  dynamic = ["version"]
8
8
  description = "Transform plain language specifications into working code"
9
9
  readme = "README.md"
10
- requires-python = ">=3.11"
10
+ requires-python = "==3.11"
11
11
  classifiers = [
12
12
  "Environment :: Console",
13
13
  "Intended Audience :: Developers",
@@ -26,6 +26,7 @@ dependencies = [
26
26
  "rich==15.0.0",
27
27
  "python-frontmatter==1.3.0",
28
28
  "networkx==3.6.1",
29
+ "sentry-sdk==2.62.0",
29
30
  ]
30
31
 
31
32
  [project.optional-dependencies]
@@ -42,6 +42,7 @@ class CodeRenderer:
42
42
  def run(self):
43
43
  """Execute the main rendering workflow."""
44
44
  self.render_context.event_bus.publish(RenderModuleStarted(module_name=self.render_context.module_name))
45
+ self.render_context.run_state.current_module = self.render_context.module_name
45
46
  previous_action_payload = None
46
47
  previous_state = None
47
48
 
@@ -63,6 +64,7 @@ class CodeRenderer:
63
64
  )
64
65
  )
65
66
  previous_state = deepcopy(self.render_context.state)
67
+ self.render_context.run_state.current_render_state = self.render_context.state
66
68
  self.render_context.script_execution_history.should_update_script_outputs = False
67
69
 
68
70
  self.render_context.previous_action_payload = previous_action_payload
@@ -1,6 +1,6 @@
1
1
  import threading
2
2
  from copy import deepcopy
3
- from typing import Optional
3
+ from typing import Callable, Optional
4
4
 
5
5
  import file_utils
6
6
  import git_utils
@@ -162,6 +162,7 @@ class RenderContext:
162
162
  linked_resources=linked_resources,
163
163
  functional_requirement_render_attempts=0,
164
164
  )
165
+ self.run_state.current_frid = frid
165
166
  return
166
167
 
167
168
  def check_frid_iteration_limit(self):
@@ -195,19 +196,10 @@ class RenderContext:
195
196
  def should_run_conformance_tests(self) -> bool:
196
197
  return self.conformance_tests_script is not None
197
198
 
198
- def finish_frid_implementation_step(self):
199
- pass
200
-
201
199
  def start_unittests_processing(self):
202
200
  self.unit_tests_running_context = UnitTestsRunningContext(fix_attempts=0)
203
201
  self.run_state.increment_unittest_batch_id()
204
202
 
205
- def start_unittests_processing_in_implementation(self):
206
- self.start_unittests_processing()
207
-
208
- def start_unittests_processing_in_refactoring(self):
209
- self.start_unittests_processing()
210
-
211
203
  def _get_first_frid_conformance_test_running_context(self, module: PlainModule | None):
212
204
  conformance_tests_running_context = self.conformance_tests_running_context
213
205
 
@@ -280,9 +272,6 @@ class RenderContext:
280
272
 
281
273
  return conformance_tests_running_context
282
274
 
283
- def start_unittests_processing_in_conformance_tests(self):
284
- self.start_unittests_processing()
285
-
286
275
  def finish_unittests_processing(self):
287
276
  existing_files = file_utils.list_all_text_files(self.build_folder)
288
277
 
@@ -294,39 +283,32 @@ class RenderContext:
294
283
  self.frid_context.changed_files.add(file_name)
295
284
  self.unit_tests_running_context.fix_attempts = 1
296
285
 
297
- def finish_unittests_processing_during_implementation(self):
298
- self.finish_unittests_processing()
299
-
300
- def start_fixing_unit_tests(self):
286
+ def start_fixing_unit_tests(self, on_limit_exceeded: Callable):
301
287
  self.unit_tests_running_context.fix_attempts += 1
302
-
303
288
  if self.unit_tests_running_context.fix_attempts > MAX_UNITTEST_FIX_ATTEMPTS:
304
- self.machine.dispatch(triggers.RESTART_FRID_PROCESSING)
289
+ on_limit_exceeded()
305
290
 
306
- def start_fixing_unit_tests_in_conformance_tests(self):
307
- self.unit_tests_running_context.fix_attempts += 1
291
+ def _on_unit_test_limit_exceeded_in_implementation(self):
292
+ self.machine.dispatch(triggers.RESTART_FRID_PROCESSING)
308
293
 
309
- if self.unit_tests_running_context.fix_attempts > MAX_UNITTEST_FIX_ATTEMPTS:
310
- self.functional_requirements_render_attempts_failed_unit_during_conformance_tests += 1
311
- if (
312
- self.functional_requirements_render_attempts_failed_unit_during_conformance_tests
313
- >= MAX_FUNCTIONAL_REQUIREMENT_RENDER_ATTEMPTS_FAILED_UNIT_DURING_CONFORMANCE_TESTS
314
- ):
315
- error_msg = f"Failed to adjust unit tests after implementation code was update while fixing conformance tests for functionality {self.frid_context.frid} for the {MAX_FUNCTIONAL_REQUIREMENT_RENDER_ATTEMPTS_FAILED_UNIT_DURING_CONFORMANCE_TESTS} times."
316
- self.dispatch_error(error_msg)
317
- else:
318
- console.info(
319
- f"Failed to adjust unit tests after implementation code was update while fixing conformance tests for functionality {self.frid_context.frid}."
320
- )
321
- console.info(f"Restarting rendering the functionality {self.frid_context.frid} from scratch.")
322
- self.machine.dispatch(triggers.RESTART_FRID_PROCESSING)
323
-
324
- def start_fixing_unit_tests_in_refactoring(self):
325
- self.unit_tests_running_context.fix_attempts += 1
294
+ def _on_unit_test_limit_exceeded_in_conformance_tests(self):
295
+ self.functional_requirements_render_attempts_failed_unit_during_conformance_tests += 1
296
+ if (
297
+ self.functional_requirements_render_attempts_failed_unit_during_conformance_tests
298
+ >= MAX_FUNCTIONAL_REQUIREMENT_RENDER_ATTEMPTS_FAILED_UNIT_DURING_CONFORMANCE_TESTS
299
+ ):
300
+ error_msg = f"Failed to adjust the unit tests after implementation code was update while fixing the conformance tests for functionality {self.frid_context.frid} for the {MAX_FUNCTIONAL_REQUIREMENT_RENDER_ATTEMPTS_FAILED_UNIT_DURING_CONFORMANCE_TESTS} times."
301
+ self.dispatch_error(error_msg)
302
+ else:
303
+ console.info(
304
+ f"Failed to adjust the unit tests after implementation code was updated while fixing the conformance tests for functionality {self.frid_context.frid}."
305
+ )
306
+ console.info(f"Restarting rendering the functionality {self.frid_context.frid} from scratch.")
307
+ self.machine.dispatch(triggers.RESTART_FRID_PROCESSING)
326
308
 
327
- if self.unit_tests_running_context.fix_attempts > MAX_UNITTEST_FIX_ATTEMPTS:
328
- git_utils.revert_changes(self.build_folder)
329
- self.machine.dispatch(triggers.START_NEW_REFACTORING_ITERATION)
309
+ def _on_unit_test_limit_exceeded_in_refactoring(self):
310
+ git_utils.revert_changes(self.build_folder)
311
+ self.machine.dispatch(triggers.START_NEW_REFACTORING_ITERATION)
330
312
 
331
313
  def start_refactoring_code(self):
332
314
 
@@ -341,9 +323,6 @@ class RenderContext:
341
323
  )
342
324
  self.machine.dispatch(triggers.PROCEED_FRID_PROCESSING)
343
325
 
344
- def finish_refactoring_code(self):
345
- pass
346
-
347
326
  def start_testing_environment_preparation(self):
348
327
  if (
349
328
  self.prepare_environment_script is None
@@ -5,7 +5,6 @@ This module defines the hierarchical state machine structure, transitions, and a
5
5
  used by the CodeRenderer to orchestrate the code generation workflow.
6
6
  """
7
7
 
8
- from dataclasses import dataclass
9
8
  from typing import Any, Callable, Dict, List
10
9
 
11
10
  import git_utils
@@ -30,46 +29,6 @@ from render_machine.render_context import RenderContext
30
29
  from render_machine.states import States
31
30
 
32
31
 
33
- @dataclass
34
- class UnitTestsStateConfig:
35
- """Dataclass for unit test state configuration."""
36
-
37
- unit_tests_failed_on_enter_function: Callable
38
- on_enter_action: Callable
39
- on_exit_action: Callable
40
-
41
-
42
- class UnitTestsConfig:
43
- """Provides configurations for different unit test scenarios."""
44
-
45
- @staticmethod
46
- def for_refactoring(render_context: RenderContext) -> UnitTestsStateConfig:
47
- """Configuration for unit tests during refactoring."""
48
- return UnitTestsStateConfig(
49
- unit_tests_failed_on_enter_function=render_context.start_fixing_unit_tests_in_refactoring,
50
- on_enter_action=render_context.start_unittests_processing_in_refactoring,
51
- on_exit_action=render_context.finish_unittests_processing,
52
- )
53
-
54
- @staticmethod
55
- def for_conformance_tests(render_context: RenderContext) -> UnitTestsStateConfig:
56
- """Configuration for unit tests during conformance tests."""
57
- return UnitTestsStateConfig(
58
- unit_tests_failed_on_enter_function=render_context.start_fixing_unit_tests_in_conformance_tests,
59
- on_enter_action=render_context.start_unittests_processing_in_conformance_tests,
60
- on_exit_action=render_context.finish_unittests_processing,
61
- )
62
-
63
- @staticmethod
64
- def for_implementation(render_context: RenderContext) -> UnitTestsStateConfig:
65
- """Configuration for unit tests during initial implementation."""
66
- return UnitTestsStateConfig(
67
- unit_tests_failed_on_enter_function=render_context.start_fixing_unit_tests,
68
- on_enter_action=render_context.start_unittests_processing_in_implementation,
69
- on_exit_action=render_context.finish_unittests_processing_during_implementation,
70
- )
71
-
72
-
73
32
  class StateMachineConfig:
74
33
  """Configuration class for the render state machine."""
75
34
 
@@ -137,26 +96,21 @@ class StateMachineConfig:
137
96
  AnalyzeSpecificationAmbiguity.SUCCESSFUL_OUTCOME: triggers.PROCEED_FRID_PROCESSING,
138
97
  }
139
98
 
140
- def get_processing_unit_tests_states(self, config: UnitTestsStateConfig) -> Dict[str, Any]:
141
- """Create the processing unit tests state configuration based on the provided configuration.
142
-
143
- Args:
144
- config: A dataclass containing the configuration for the unit test state.
145
-
146
- Returns:
147
- Dictionary defining the processing unit tests hierarchical state.
148
- """
149
- children = [
150
- States.UNIT_TESTS_READY.value,
151
- {"name": States.UNIT_TESTS_FAILED.value, "on_enter": config.unit_tests_failed_on_enter_function},
152
- ]
153
-
99
+ def get_processing_unit_tests_states(
100
+ self, render_context: RenderContext, on_limit_exceeded: Callable
101
+ ) -> Dict[str, Any]:
154
102
  return {
155
103
  "name": States.PROCESSING_UNIT_TESTS.value,
156
104
  "initial": States.UNIT_TESTS_READY.value,
157
- "on_enter": config.on_enter_action,
158
- "on_exit": config.on_exit_action,
159
- "children": children,
105
+ "on_enter": render_context.start_unittests_processing,
106
+ "on_exit": render_context.finish_unittests_processing,
107
+ "children": [
108
+ States.UNIT_TESTS_READY.value,
109
+ {
110
+ "name": States.UNIT_TESTS_FAILED.value,
111
+ "on_enter": lambda: render_context.start_fixing_unit_tests(on_limit_exceeded),
112
+ },
113
+ ],
160
114
  }
161
115
 
162
116
  def get_postprocessing_conformance_tests_states(self) -> Dict[str, Any]:
@@ -191,7 +145,9 @@ class StateMachineConfig:
191
145
  "on_enter": render_context.start_fixing_conformance_tests,
192
146
  "on_exit": render_context.finish_fixing_conformance_tests,
193
147
  },
194
- self.get_processing_unit_tests_states(UnitTestsConfig.for_conformance_tests(render_context)),
148
+ self.get_processing_unit_tests_states(
149
+ render_context, render_context._on_unit_test_limit_exceeded_in_conformance_tests
150
+ ),
195
151
  self.get_postprocessing_conformance_tests_states(),
196
152
  ],
197
153
  }
@@ -209,10 +165,11 @@ class StateMachineConfig:
209
165
  "name": States.REFACTORING_CODE.value,
210
166
  "initial": States.READY_FOR_REFACTORING.value,
211
167
  "on_enter": render_context.start_refactoring_code,
212
- "on_exit": render_context.finish_refactoring_code,
213
168
  "children": [
214
169
  States.READY_FOR_REFACTORING.value,
215
- self.get_processing_unit_tests_states(UnitTestsConfig.for_refactoring(render_context)),
170
+ self.get_processing_unit_tests_states(
171
+ render_context, render_context._on_unit_test_limit_exceeded_in_refactoring
172
+ ),
216
173
  States.STEP_COMPLETED.value,
217
174
  ],
218
175
  }
@@ -225,12 +182,14 @@ class StateMachineConfig:
225
182
  "on_enter": render_context.start_implementing_frid,
226
183
  "on_exit": render_context.finish_implementing_frid,
227
184
  "children": [
228
- {"name": States.STEP_COMPLETED.value, "on_exit": render_context.finish_frid_implementation_step},
185
+ {"name": States.STEP_COMPLETED.value},
229
186
  {
230
187
  "name": States.READY_FOR_FRID_IMPLEMENTATION.value,
231
188
  "on_enter": render_context.check_frid_iteration_limit,
232
189
  },
233
- self.get_processing_unit_tests_states(UnitTestsConfig.for_implementation(render_context)),
190
+ self.get_processing_unit_tests_states(
191
+ render_context, render_context._on_unit_test_limit_exceeded_in_implementation
192
+ ),
234
193
  refactoring_code_states,
235
194
  self.get_processing_conformance_tests_states(render_context),
236
195
  States.FRID_FULLY_IMPLEMENTED.value,
@@ -7,7 +7,7 @@ that controls the code rendering process.
7
7
 
8
8
  # Trigger constants for state machine transitions
9
9
  START_RENDER = "start_render"
10
- RENDER_FUNCTIONAL_REQUIREMENT = "render_functional_requirement" # todo: rename
10
+ RENDER_FUNCTIONAL_REQUIREMENT = "render_functional_requirement"
11
11
  PROCEED_FRID_PROCESSING = "proceed_frid_processing"
12
12
  MARK_UNIT_TESTS_FAILED = "mark_unit_tests_failed"
13
13
  MARK_UNIT_TESTS_PASSED = "mark_unit_tests_passed"
@@ -10,6 +10,7 @@ pytest==8.3.4
10
10
  textual>=7.5.0
11
11
  networkx==3.6.1
12
12
  transitions==0.9.3
13
+ sentry-sdk==2.62.0
13
14
 
14
15
 
15
16
  # Development dependencies