codeplain 0.3.11.dev20__tar.gz → 0.3.11.dev22__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 (285) hide show
  1. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/PKG-INFO +1 -1
  2. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/codeplain_REST_api.py +32 -26
  3. codeplain-0.3.11.dev22/render_machine/actions/fix_unit_tests.py +79 -0
  4. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/actions/run_unit_tests.py +0 -9
  5. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/render_context.py +0 -9
  6. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/render_types.py +0 -45
  7. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_fix_unit_tests_conformance_context.py +25 -120
  8. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_tui_components.py +60 -1
  9. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tui/components.py +25 -2
  10. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tui/plain2code_tui.py +9 -0
  11. codeplain-0.3.11.dev20/render_machine/actions/fix_unit_tests.py +0 -256
  12. codeplain-0.3.11.dev20/render_machine/agent_tools.py +0 -270
  13. codeplain-0.3.11.dev20/tests/test_agent_tools.py +0 -166
  14. codeplain-0.3.11.dev20/tests/test_fix_unit_tests_action.py +0 -239
  15. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/.claude/settings.json +0 -0
  16. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/.flake8 +0 -0
  17. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/.github/dependabot.yml +0 -0
  18. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/.github/workflows/e2e.yml +0 -0
  19. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/.github/workflows/lint-and-test.yml +0 -0
  20. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/.github/workflows/lint-powershell.yml +0 -0
  21. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/.github/workflows/nofity-slack-on-main-merge.yml +0 -0
  22. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/.github/workflows/publish-dev-to-pypi.yml +0 -0
  23. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/.github/workflows/publish-install-script.yml +0 -0
  24. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/.github/workflows/publish-to-pypi.yml +0 -0
  25. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/.gitignore +0 -0
  26. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/.vscode/launch.json +0 -0
  27. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/.vscode/settings.json +0 -0
  28. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/CLAUDE.md +0 -0
  29. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/CONTRIBUTING.md +0 -0
  30. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/LICENSE +0 -0
  31. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/README.md +0 -0
  32. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/change_detection.py +0 -0
  33. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/cli_output/__init__.py +0 -0
  34. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/cli_output/dry_run.py +0 -0
  35. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/cli_output/render_summary.py +0 -0
  36. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/cli_output/status.py +0 -0
  37. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/concept_utils.py +0 -0
  38. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/config/__init__.py +0 -0
  39. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/config/system_config.yaml +0 -0
  40. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/conformance_fix_journal.py +0 -0
  41. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/diff_utils.py +0 -0
  42. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/docs/generate_cli.py +0 -0
  43. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/docs/plain2code_cli.md +0 -0
  44. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/docs/starting_a_plain_project_from_scratch.md +0 -0
  45. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/event_bus.py +0 -0
  46. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/examples/example_hello_world_golang/config.yaml +0 -0
  47. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/examples/example_hello_world_golang/harness_tests/hello_world_test.go +0 -0
  48. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/examples/example_hello_world_golang/hello_world_golang.plain +0 -0
  49. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/examples/example_hello_world_golang/run.sh +0 -0
  50. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/examples/example_hello_world_python/config.yaml +0 -0
  51. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/examples/example_hello_world_python/harness_tests/hello_world_display/test_hello_world.py +0 -0
  52. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/examples/example_hello_world_python/hello_world_python.plain +0 -0
  53. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/examples/example_hello_world_python/run.sh +0 -0
  54. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/examples/example_hello_world_react/config.yaml +0 -0
  55. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress/e2e/hello_world.cy.ts +0 -0
  56. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress/support/e2e.ts +0 -0
  57. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress.config.ts +0 -0
  58. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/examples/example_hello_world_react/harness_tests/hello_world_display/package.json +0 -0
  59. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/examples/example_hello_world_react/harness_tests/hello_world_display/tsconfig.json +0 -0
  60. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/examples/example_hello_world_react/hello_world_react.plain +0 -0
  61. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/examples/example_hello_world_react/run.sh +0 -0
  62. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/examples/run.sh +0 -0
  63. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/file_utils.py +0 -0
  64. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/git_preflight.py +0 -0
  65. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/git_utils.py +0 -0
  66. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/install/bash/examples.sh +0 -0
  67. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/install/bash/install.sh +0 -0
  68. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/install/bash/walkthrough.sh +0 -0
  69. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/install/powershell/examples.ps1 +0 -0
  70. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/install/powershell/install.ps1 +0 -0
  71. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/install/powershell/walkthrough.ps1 +0 -0
  72. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/memory_management.py +0 -0
  73. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/metadata_utils.py +0 -0
  74. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/module_renderer.py +0 -0
  75. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/partial_rendering.py +0 -0
  76. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/path_resolution.py +0 -0
  77. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/plain2code.py +0 -0
  78. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/plain2code_arguments.py +0 -0
  79. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/plain2code_cli.md +0 -0
  80. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/plain2code_console.py +0 -0
  81. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/plain2code_events.py +0 -0
  82. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/plain2code_exceptions.py +0 -0
  83. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/plain2code_logger.py +0 -0
  84. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/plain2code_nodes.py +0 -0
  85. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/plain2code_read_config.py +0 -0
  86. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/plain2code_state.py +0 -0
  87. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/plain2code_telemetry.py +0 -0
  88. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/plain2code_utils.py +0 -0
  89. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/plain_file.py +0 -0
  90. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/plain_modules.py +0 -0
  91. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/plain_spec.py +0 -0
  92. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/pyproject.toml +0 -0
  93. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/pytest.ini +0 -0
  94. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/__init__.py +0 -0
  95. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/actions/analyze_specification_ambiguity.py +0 -0
  96. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/actions/base_action.py +0 -0
  97. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/actions/commit_conformance_tests_changes.py +0 -0
  98. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/actions/commit_implementation_code_changes.py +0 -0
  99. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/actions/create_dist.py +0 -0
  100. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/actions/distill_conformance_test_memory.py +0 -0
  101. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/actions/exit_with_error.py +0 -0
  102. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/actions/finish_functional_requirement.py +0 -0
  103. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/actions/fix_conformance_test.py +0 -0
  104. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/actions/prepare_repositories.py +0 -0
  105. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/actions/prepare_testing_environment.py +0 -0
  106. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/actions/refactor_code.py +0 -0
  107. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/actions/render_conformance_tests.py +0 -0
  108. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/actions/render_functional_requirement.py +0 -0
  109. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/actions/run_conformance_tests.py +0 -0
  110. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/actions/summarize_conformance_tests.py +0 -0
  111. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/code_renderer.py +0 -0
  112. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/conformance_tests.py +0 -0
  113. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/implementation_code_helpers.py +0 -0
  114. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/render_utils.py +0 -0
  115. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/state_machine_config.py +0 -0
  116. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/states.py +0 -0
  117. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/render_machine/triggers.py +0 -0
  118. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/requirements.txt +0 -0
  119. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/resources/codeplain_overview.png +0 -0
  120. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/resources/plain_example.png +0 -0
  121. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/standard_template_library/golang-console-app-template.plain +0 -0
  122. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/standard_template_library/python-console-app-template.plain +0 -0
  123. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/standard_template_library/typescript-react-app-boilerplate.plain +0 -0
  124. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/standard_template_library/typescript-react-app-template.plain +0 -0
  125. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/system_config.py +0 -0
  126. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/test_scripts/run_conformance_tests_cypress.ps1 +0 -0
  127. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/test_scripts/run_conformance_tests_cypress.sh +0 -0
  128. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/test_scripts/run_conformance_tests_golang.ps1 +0 -0
  129. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/test_scripts/run_conformance_tests_golang.sh +0 -0
  130. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/test_scripts/run_conformance_tests_python.ps1 +0 -0
  131. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/test_scripts/run_conformance_tests_python.sh +0 -0
  132. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/test_scripts/run_unittests_flutter.ps1 +0 -0
  133. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/test_scripts/run_unittests_flutter.sh +0 -0
  134. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/test_scripts/run_unittests_golang.ps1 +0 -0
  135. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/test_scripts/run_unittests_golang.sh +0 -0
  136. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/test_scripts/run_unittests_python.ps1 +0 -0
  137. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/test_scripts/run_unittests_python.sh +0 -0
  138. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/test_scripts/run_unittests_react.ps1 +0 -0
  139. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/test_scripts/run_unittests_react.sh +0 -0
  140. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/__init__.py +0 -0
  141. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/conftest.py +0 -0
  142. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/acceptance_tests_warning/main_requiring_acceptance_tests.plain +0 -0
  143. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/acceptance_tests_warning/required_with_acceptance_tests.plain +0 -0
  144. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/imports/circular_imports_1.plain +0 -0
  145. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/imports/circular_imports_2.plain +0 -0
  146. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/imports/circular_imports_main.plain +0 -0
  147. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/imports/diamond_import_1.plain +0 -0
  148. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/imports/diamond_import_2.plain +0 -0
  149. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/imports/diamond_import_common.plain +0 -0
  150. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/imports/diamond_imports_main.plain +0 -0
  151. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/imports/import_with_functionalities.plain +0 -0
  152. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/imports/imports_module_with_functionalities.plain +0 -0
  153. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/imports/non_existent_import.plain +0 -0
  154. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/partial_rendering/pr_code_var.plain +0 -0
  155. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/partial_rendering/pr_implement.plain +0 -0
  156. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/partial_rendering/pr_leaf.plain +0 -0
  157. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/partial_rendering/pr_middle.plain +0 -0
  158. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/partial_rendering/pr_root.plain +0 -0
  159. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/partial_rendering/pr_solo.plain +0 -0
  160. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfile/add_new_task_modal_specification.yaml +0 -0
  161. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfile/duplicate_specification_heading.plain +0 -0
  162. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfile/empty_functional_specs_section.plain +0 -0
  163. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfile/invalid_specification_order.plain +0 -0
  164. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfile/missing_non_functional_requirements.plain +0 -0
  165. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfile/no_functional_specs_section.plain +0 -0
  166. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfile/plain_source_with_absolute_link.plain +0 -0
  167. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfile/plain_source_with_missing_link.plain +0 -0
  168. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfile/plain_source_with_url_link.plain +0 -0
  169. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfile/task_list_ui_specification.yaml +0 -0
  170. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfile/task_manager_with_reference_links.plain +0 -0
  171. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfile/without_non_functional_requirement.plain +0 -0
  172. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/concept_validation_acceptance_tests.plain +0 -0
  173. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/concept_validation_acceptance_tests_nondefined.plain +0 -0
  174. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/concept_validation_defined_nondefined.plain +0 -0
  175. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/concept_validation_defined_nondefined_2.plain +0 -0
  176. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/concept_validation_definition.plain +0 -0
  177. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/concept_validation_noconcepts.plain +0 -0
  178. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/concept_validation_nonconcept.plain +0 -0
  179. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/concept_validation_nondefined.plain +0 -0
  180. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/concept_validation_redefinition.plain +0 -0
  181. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/concept_validation_several_concepts.plain +0 -0
  182. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/concept_validation_valid.plain +0 -0
  183. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/cyclic_definitions.plain +0 -0
  184. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/exported_concepts.plain +0 -0
  185. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/exported_concepts_base.plain +0 -0
  186. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/exported_concepts_defaults_exported.plain +0 -0
  187. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/exported_concepts_example.plain +0 -0
  188. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/exported_concepts_imported_exported.plain +0 -0
  189. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/exported_concepts_inherited_reexported.plain +0 -0
  190. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/exported_concepts_malformed.plain +0 -0
  191. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/exported_concepts_missing_definition.plain +0 -0
  192. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/exported_concepts_missing_definition_example.plain +0 -0
  193. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/exported_concepts_multiple_per_bullet.plain +0 -0
  194. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/exported_concepts_multiple_per_bullet_undefined.plain +0 -0
  195. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/exported_concepts_nested.plain +0 -0
  196. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/exported_concepts_nested_example.plain +0 -0
  197. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/exported_concepts_no_definitions.plain +0 -0
  198. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/exported_concepts_not_declared.plain +0 -0
  199. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/exported_concepts_not_declared_example.plain +0 -0
  200. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/exported_concepts_transitive_example.plain +0 -0
  201. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/exported_concepts_transitive_l1.plain +0 -0
  202. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/exported_concepts_transitive_l2.plain +0 -0
  203. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/plain_file_parser_with_comments.plain +0 -0
  204. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/plain_file_with_comments_indented.plain +0 -0
  205. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/regular_plain_source.plain +0 -0
  206. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/required_concepts.plain +0 -0
  207. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/required_concepts_defs.plain +0 -0
  208. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/required_concepts_example.plain +0 -0
  209. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/required_concepts_l1.plain +0 -0
  210. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/required_concepts_l2.plain +0 -0
  211. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/required_concepts_missing.plain +0 -0
  212. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/required_concepts_module.plain +0 -0
  213. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/required_concepts_partial.plain +0 -0
  214. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/required_concepts_partial_duplicate.plain +0 -0
  215. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/requires_without_definitions_example.plain +0 -0
  216. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/topological_sort.plain +0 -0
  217. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/plainfileparser/topological_sort_not_referenced.plain +0 -0
  218. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/requires/circular_requires_main.plain +0 -0
  219. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/requires/circular_requires_sub.plain +0 -0
  220. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/requires/diamond_requires_1.plain +0 -0
  221. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/requires/diamond_requires_2.plain +0 -0
  222. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/requires/diamond_requires_common.plain +0 -0
  223. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/requires/diamond_requires_main.plain +0 -0
  224. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/requires/independent_requires_1.plain +0 -0
  225. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/requires/independent_requires_2.plain +0 -0
  226. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/requires/independent_requires_main.plain +0 -0
  227. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/requires/non_existent_require.plain +0 -0
  228. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/requires/normal_requires_1.plain +0 -0
  229. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/requires/normal_requires_2.plain +0 -0
  230. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/requires/normal_requires_common.plain +0 -0
  231. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/requires/normal_requires_main.plain +0 -0
  232. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/sample_base64_image.txt +0 -0
  233. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/simple.plain +0 -0
  234. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/templates/block_level_include.plain +0 -0
  235. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/templates/code_variables.plain +0 -0
  236. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/templates/header.plain +0 -0
  237. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/templates/implement.plain +0 -0
  238. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/templates/implement_2.plain +0 -0
  239. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/templates/template_include.plain +0 -0
  240. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/data/templates/test_hardest_problem.plain +0 -0
  241. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/e2e/Dockerfile +0 -0
  242. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/e2e/conftest.py +0 -0
  243. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/e2e/test_hello_world_python.py +0 -0
  244. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/e2e/test_hello_world_python_windows.py +0 -0
  245. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_arg_provenance.py +0 -0
  246. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_change_detection.py +0 -0
  247. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_cli_output.py +0 -0
  248. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_conformance_fix_journal.py +0 -0
  249. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_console_log_styles.py +0 -0
  250. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_distill_conformance_test_memory.py +0 -0
  251. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_dry_run_link_validation.py +0 -0
  252. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_file_utils.py +0 -0
  253. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_folder_path_resolution.py +0 -0
  254. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_git_preflight.py +0 -0
  255. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_git_utils.py +0 -0
  256. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_headless_logging.py +0 -0
  257. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_imports.py +0 -0
  258. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_log_file_name_resolution.py +0 -0
  259. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_logging_config_path_resolution.py +0 -0
  260. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_partial_rendering.py +0 -0
  261. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_path_resolution.py +0 -0
  262. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_plain2code.py +0 -0
  263. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_plain2code_state.py +0 -0
  264. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_plain2code_utils.py +0 -0
  265. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_plain_modules.py +0 -0
  266. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_plainfile.py +0 -0
  267. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_plainfileparser.py +0 -0
  268. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_plainspec.py +0 -0
  269. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_prepare_repositories_layout.py +0 -0
  270. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_render_finished_report.py +0 -0
  271. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_requires.py +0 -0
  272. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_resolve_config_file.py +0 -0
  273. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_script_path_resolution.py +0 -0
  274. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_system_config.py +0 -0
  275. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_telemetry.py +0 -0
  276. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_tui_usage.py +0 -0
  277. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tests/test_usage_summary.py +0 -0
  278. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tui/__init__.py +0 -0
  279. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tui/models.py +0 -0
  280. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tui/plain_module_render_choice_tui.py +0 -0
  281. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tui/spinner.py +0 -0
  282. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tui/state_handlers.py +0 -0
  283. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tui/styles.css +0 -0
  284. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/tui/widget_helpers.py +0 -0
  285. {codeplain-0.3.11.dev20 → codeplain-0.3.11.dev22}/usage_summary.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: codeplain
3
- Version: 0.3.11.dev20
3
+ Version: 0.3.11.dev22
4
4
  Summary: Transform plain language specifications into working code
5
5
  License-File: LICENSE
6
6
  Classifier: Environment :: Console
@@ -253,6 +253,38 @@ class CodeplainAPI:
253
253
 
254
254
  return self.post_request(endpoint_url, headers, payload, run_state)
255
255
 
256
+ def fix_unittests_issue(
257
+ self,
258
+ frid,
259
+ plain_source_tree,
260
+ linked_resources,
261
+ existing_files_content,
262
+ module_name: str,
263
+ required_modules,
264
+ unittests_issue,
265
+ run_state: RunState,
266
+ conformance_tests_fixes: list[dict] | None = None,
267
+ ):
268
+ endpoint_url = f"{self.api_url}/fix_unittests_issue"
269
+ headers = {"X-API-Key": self.api_key, "Content-Type": "application/json"}
270
+
271
+ payload = {
272
+ "frid": frid,
273
+ "plain_source_tree": plain_source_tree,
274
+ "linked_resources": linked_resources,
275
+ "existing_files_content": existing_files_content,
276
+ "module_name": module_name,
277
+ "required_modules": required_modules,
278
+ "unittests_issue": unittests_issue,
279
+ "unittest_batch_id": run_state.unittest_batch_id,
280
+ }
281
+ # Implementation code changes made by the conformance tests fixer right before this unit tests run;
282
+ # only sent when unit tests are processed inside the conformance tests phase.
283
+ if conformance_tests_fixes is not None:
284
+ payload["conformance_tests_fixes"] = conformance_tests_fixes
285
+
286
+ return self.post_request(endpoint_url, headers, payload, run_state)
287
+
256
288
  def distill_conformance_test_memory(
257
289
  self,
258
290
  frid,
@@ -526,29 +558,3 @@ class CodeplainAPI:
526
558
  }
527
559
 
528
560
  return self.post_request(endpoint_url, headers, payload, run_state)
529
-
530
- def agent_start(self, task_type: str, task_params: dict, frid: str, module_name: str, run_state: RunState):
531
- """Start a server-side agent session; returns the first turn (tool calls or completion)."""
532
- endpoint_url = f"{self.api_url}/agent/start"
533
- headers = {"X-API-Key": self.api_key, "Content-Type": "application/json"}
534
- payload = {
535
- "task_type": task_type,
536
- "task_params": task_params,
537
- "frid": frid,
538
- "module_name": module_name,
539
- }
540
- return self.post_request(endpoint_url, headers, payload, run_state)
541
-
542
- def agent_continue(
543
- self, session_id: str, tool_results: list[dict], frid: str, module_name: str, run_state: RunState
544
- ):
545
- """Feed tool results into an agent session and run its next turn."""
546
- endpoint_url = f"{self.api_url}/agent/continue"
547
- headers = {"X-API-Key": self.api_key, "Content-Type": "application/json"}
548
- payload = {
549
- "session_id": session_id,
550
- "tool_results": tool_results,
551
- "frid": frid,
552
- "module_name": module_name,
553
- }
554
- return self.post_request(endpoint_url, headers, payload, run_state)
@@ -0,0 +1,79 @@
1
+ from typing import Any
2
+
3
+ import file_utils
4
+ import render_machine.render_utils as render_utils
5
+ from plain2code_console import console
6
+ from plain2code_exceptions import InternalClientError
7
+ from render_machine.actions.base_action import BaseAction
8
+ from render_machine.implementation_code_helpers import ImplementationCodeHelpers
9
+ from render_machine.render_context import RenderContext
10
+
11
+ MAX_ISSUE_LENGTH = 10000
12
+
13
+
14
+ class FixUnitTests(BaseAction):
15
+ SUCCESSFUL_OUTCOME = "unit_tests_fix_generated"
16
+
17
+ def execute(self, render_context: RenderContext, previous_action_payload: Any | None):
18
+ if not previous_action_payload.get("previous_unittests_issue"):
19
+ raise InternalClientError(
20
+ "Internal client error: Previous action payload does not contain previous unit tests issue."
21
+ )
22
+ previous_unittests_issue = previous_action_payload["previous_unittests_issue"]
23
+
24
+ if previous_unittests_issue and len(previous_unittests_issue) > MAX_ISSUE_LENGTH:
25
+ console.debug(
26
+ f"Unit tests issue text is too long and will be smartly truncated to {MAX_ISSUE_LENGTH} characters."
27
+ )
28
+
29
+ existing_files, existing_files_content = ImplementationCodeHelpers.fetch_existing_files(
30
+ render_context.build_folder
31
+ )
32
+
33
+ render_utils.print_inputs(render_context, existing_files_content, "Files sent as input to unit tests fixing:")
34
+
35
+ conformance_tests_fixes = self._get_conformance_tests_fixes(render_context)
36
+ if conformance_tests_fixes:
37
+ console.info(
38
+ f"Unit tests are fixed while preserving {len(conformance_tests_fixes)} implementation code change(s) "
39
+ "made to fix the conformance tests."
40
+ )
41
+
42
+ response_files = render_context.codeplain_api.fix_unittests_issue(
43
+ render_context.frid_context.frid,
44
+ render_context.plain_source_tree,
45
+ render_context.frid_context.linked_resources,
46
+ existing_files_content,
47
+ render_context.module_name,
48
+ render_context.get_required_modules_functionalities(),
49
+ previous_unittests_issue,
50
+ run_state=render_context.run_state,
51
+ conformance_tests_fixes=conformance_tests_fixes,
52
+ )
53
+
54
+ _, changed_files = file_utils.update_build_folder_with_rendered_files(
55
+ render_context.build_folder, existing_files, response_files
56
+ )
57
+
58
+ render_context.unit_tests_running_context.changed_files.update(changed_files)
59
+
60
+ console.print_files("Files fixed:", render_context.build_folder, response_files, style=console.OUTPUT_STYLE)
61
+
62
+ return self.SUCCESSFUL_OUTCOME, None
63
+
64
+ @staticmethod
65
+ def _get_conformance_tests_fixes(render_context: RenderContext) -> list[dict] | None:
66
+ """Implementation code changes the conformance tests fixer made before these unit tests were run.
67
+
68
+ Only present when unit tests are processed inside the conformance tests phase - the implementation
69
+ and refactoring unit test passes have no conformance tests running context.
70
+ """
71
+ conformance_tests_running_context = getattr(render_context, "conformance_tests_running_context", None)
72
+ if conformance_tests_running_context is None:
73
+ return None
74
+
75
+ implementation_code_fixes = getattr(conformance_tests_running_context, "implementation_code_fixes", None)
76
+ if not implementation_code_fixes:
77
+ return None
78
+
79
+ return list(implementation_code_fixes)
@@ -16,15 +16,6 @@ class RunUnitTests(BaseAction):
16
16
  UNRECOVERABLE_ERROR_OUTCOME = "unrecoverable_error_occurred"
17
17
 
18
18
  def execute(self, render_context: RenderContext, _previous_action_payload: Any | None):
19
- context = render_context.unit_tests_running_context
20
- if context.verified_passing:
21
- # The fixing agent's own run passed and no file changed since; running again is redundant.
22
- context.verified_passing = False
23
- console.info("Unit tests already passed in the fixing agent's last run; not running them again.")
24
- render_context.script_execution_history.latest_unit_test_output_path = context.verified_passing_log_path
25
- render_context.script_execution_history.should_update_script_outputs = True
26
- return self.SUCCESSFUL_OUTCOME, None
27
-
28
19
  unittests_script = os.path.normpath(render_context.unittests_script)
29
20
 
30
21
  console.info(
@@ -19,7 +19,6 @@ from render_machine.render_types import (
19
19
  FridContext,
20
20
  ScriptExecutionHistory,
21
21
  TestExecutionPhase,
22
- UnitTestsAgentSession,
23
22
  UnitTestsRunningContext,
24
23
  )
25
24
 
@@ -189,14 +188,6 @@ class RenderContext:
189
188
  def should_run_conformance_tests(self) -> bool:
190
189
  return self.conformance_tests_script is not None
191
190
 
192
- @property
193
- def unit_tests_agent_session(self) -> UnitTestsAgentSession:
194
- """The agent session fixing the unit tests: per unit-test loop, except during the conformance
195
- phase, where one session spans every loop (see UnitTestsAgentSession)."""
196
- if self.conformance_tests_running_context is not None:
197
- return self.conformance_tests_running_context.unit_tests_agent_session
198
- return self.unit_tests_running_context.agent_session
199
-
200
191
  def start_unittests_processing(self):
201
192
  self.unit_tests_running_context = UnitTestsRunningContext(fix_attempts=0)
202
193
  self.run_state.increment_unittest_batch_id()
@@ -48,53 +48,10 @@ class FridContext:
48
48
  refactoring_iteration: int = 0
49
49
 
50
50
 
51
- @dataclass
52
- class UnitTestsAgentSession:
53
- """Server-side agent session fixing a FRID's unit tests, and what it still has to be told.
54
-
55
- Owned by the unit-tests running context (one session per unit-test loop) in the implementation
56
- and refactoring phases, and by the conformance tests running context during the conformance
57
- phase, so there one session spans every unit-test loop - the agent then sees that the
58
- conformance tests fixer keeps changing the code it adjusts (see RenderContext.unit_tests_agent_session).
59
- """
60
-
61
- session_id: Optional[str] = None
62
- # The submit_fix call the agent ended its last attempt with, answered with the next test
63
- # run's outcome, plus results of any tool calls made in the same turn as submit_fix.
64
- pending_submit_call_id: Optional[str] = None
65
- pending_tool_results: list[dict] = field(default_factory=list)
66
- # Session abandoned without a submission (turn budget used up, LLM failure); the next session
67
- # starts with a digest of what it tried.
68
- previous_session_id: Optional[str] = None
69
- # Full test logs the agent was pointed to; readable by read_file/grep although outside the
70
- # build folder.
71
- readable_log_paths: set[str] = field(default_factory=set)
72
- # How many of the conformance tests fixes (ConformanceTestsRunningContext.implementation_code_fixes)
73
- # the session has already been shown.
74
- conformance_fixes_handed_off: int = 0
75
-
76
- def reset(self) -> None:
77
- """Drop the session (keeping its id as the previous one) so the next failure starts a new one."""
78
- self.previous_session_id = self.session_id
79
- self.session_id, self.pending_submit_call_id, self.pending_tool_results = None, None, []
80
- self.conformance_fixes_handed_off = 0
81
-
82
-
83
51
  @dataclass
84
52
  class UnitTestsRunningContext:
85
53
  fix_attempts: int
86
54
  changed_files: set[str] = field(default_factory=set)
87
- # The agent session of this loop; used only outside the conformance phase.
88
- agent_session: UnitTestsAgentSession = field(default_factory=UnitTestsAgentSession)
89
- # Whether the agent already made a fix attempt in this loop. A session that is still open when
90
- # a loop starts had its last fix accepted, which is what the agent is told.
91
- agent_used_in_this_loop: bool = False
92
- # Set when the agent's own run_unit_tests passed and no file changed since, so the harness
93
- # can accept the fix without running the suite again.
94
- verified_passing: bool = False
95
- verified_passing_log_path: Optional[str] = None
96
- # Results of read-only tool calls, keyed by call; cleared whenever a file changes.
97
- tool_result_cache: dict[str, str] = field(default_factory=dict)
98
55
 
99
56
 
100
57
  class ConformanceTestsRunningContext:
@@ -138,8 +95,6 @@ class ConformanceTestsRunningContext:
138
95
  # order. Each entry is {"hypothesis": str | None, "approach": str | None, "code_diff": {file: diff}}.
139
96
  # Handed to the unit tests fixer so it adjusts the unit tests instead of reverting these changes.
140
97
  self.implementation_code_fixes: list[dict] = []
141
- # The unit-test fixing agent session, shared by every unit-test loop of this conformance phase.
142
- self.unit_tests_agent_session = UnitTestsAgentSession()
143
98
 
144
99
  def get_conformance_tests_json(self, module_name: str) -> dict:
145
100
  return self._conformance_tests_json[module_name]
@@ -12,44 +12,25 @@ import pytest
12
12
 
13
13
  import plain_spec
14
14
  from memory_management import MemoryManager
15
+ from render_machine.actions import fix_unit_tests as fix_unit_tests_module
15
16
  from render_machine.actions.fix_conformance_test import FixConformanceTest
16
17
  from render_machine.actions.fix_unit_tests import FixUnitTests
17
18
  from render_machine.implementation_code_helpers import ImplementationCodeHelpers
18
- from render_machine.render_context import RenderContext
19
- from render_machine.render_types import ConformanceTestsRunningContext, ScriptExecutionHistory, UnitTestsRunningContext
19
+ from render_machine.render_types import ConformanceTestsRunningContext, UnitTestsRunningContext
20
20
 
21
21
 
22
22
  class FakeCodeplainAPI:
23
- """Conformance fixes return a scripted response; every agent session submits a fix on its first turn."""
24
-
25
- def __init__(self, conformance_fix_response=None):
23
+ def __init__(self, conformance_fix_response=None, unittests_fix_response=None):
26
24
  self.conformance_fix_response = conformance_fix_response
27
- self.agent_calls = []
28
- self.sessions_started = 0
25
+ self.unittests_fix_response = unittests_fix_response or {}
26
+ self.unittests_fix_calls = []
29
27
 
30
28
  def fix_conformance_tests_issue(self, *args, **kwargs):
31
29
  return self.conformance_fix_response
32
30
 
33
- def agent_start(self, task_type, task_params, frid, module_name, run_state):
34
- self.sessions_started += 1
35
- self.agent_calls.append(("start", task_params))
36
- return self._submit(f"s{self.sessions_started}")
37
-
38
- def agent_continue(self, session_id, tool_results, frid, module_name, run_state):
39
- self.agent_calls.append(("continue", session_id, tool_results))
40
- return self._submit(session_id)
41
-
42
- def _submit(self, session_id):
43
- call_id = f"submit-{len(self.agent_calls)}"
44
- return {
45
- "session_id": session_id,
46
- "status": "tool_calls",
47
- "calls": [{"id": call_id, "name": "submit_fix", "args": {"changes_made": "fixed"}}],
48
- }
49
-
50
-
51
- class FakeRenderContext(SimpleNamespace):
52
- unit_tests_agent_session = RenderContext.unit_tests_agent_session
31
+ def fix_unittests_issue(self, *args, **kwargs):
32
+ self.unittests_fix_calls.append(kwargs)
33
+ return self.unittests_fix_response
53
34
 
54
35
 
55
36
  class FakeConformanceTests:
@@ -78,11 +59,7 @@ def memory_folder():
78
59
  def isolate_from_git_and_console(monkeypatch):
79
60
  monkeypatch.setattr(ImplementationCodeHelpers, "get_code_diff", staticmethod(lambda *args: {}))
80
61
  monkeypatch.setattr(plain_spec, "collect_linked_resources", lambda *args: None)
81
- monkeypatch.setattr(
82
- plain_spec,
83
- "get_specifications_for_frid",
84
- lambda tree, frid: ({plain_spec.FUNCTIONAL_REQUIREMENTS: ["- Add numbers."]}, None),
85
- )
62
+ monkeypatch.setattr(fix_unit_tests_module.render_utils, "print_inputs", lambda *args: None)
86
63
 
87
64
 
88
65
  def make_conformance_context():
@@ -99,7 +76,7 @@ def make_conformance_context():
99
76
 
100
77
 
101
78
  def make_render_context(api, build_folder, memory_folder, conformance_tests_running_context):
102
- return FakeRenderContext(
79
+ return SimpleNamespace(
103
80
  codeplain_api=api,
104
81
  build_folder=build_folder,
105
82
  memory_manager=MemoryManager(api, memory_folder),
@@ -109,11 +86,9 @@ def make_render_context(api, build_folder, memory_folder, conformance_tests_runn
109
86
  plain_source_tree={},
110
87
  module_name="mod",
111
88
  required_modules=None,
112
- frid_context=SimpleNamespace(frid="1", linked_resources={}, changed_files=set()),
89
+ frid_context=SimpleNamespace(frid="1", linked_resources={}),
113
90
  get_required_modules_functionalities=lambda: {},
114
91
  run_state=SimpleNamespace(render_id="test-render-id", unittest_batch_id=1),
115
- unittests_script=None,
116
- script_execution_history=ScriptExecutionHistory(),
117
92
  )
118
93
 
119
94
 
@@ -216,104 +191,34 @@ def test_implementation_fix_with_no_files_is_not_remembered(build_folder, memory
216
191
  assert ctx.implementation_code_fixes == []
217
192
 
218
193
 
219
- def run_unit_tests_fix(render_context, issue="1 failed"):
220
- return FixUnitTests().execute(render_context, {"previous_unittests_issue": issue})
221
-
222
-
223
- def start_new_unit_test_loop(render_context):
224
- """What RenderContext.start_unittests_processing does when the unit tests are run again."""
225
- render_context.unit_tests_running_context = UnitTestsRunningContext(fix_attempts=0)
226
-
227
-
228
- def conformance_fix(number):
229
- return {
230
- "hypothesis": f"hypothesis {number}",
231
- "approach": f"approach {number}",
232
- "code_diff": {"app.py": f"+{number}"},
233
- }
194
+ def run_unit_tests_fix(render_context):
195
+ return FixUnitTests().execute(render_context, {"previous_unittests_issue": "1 failed"})
234
196
 
235
197
 
236
- def test_first_unit_test_loop_of_conformance_phase_seeds_the_session_with_the_fixes(build_folder, memory_folder):
237
- api = FakeCodeplainAPI()
198
+ def test_unit_tests_fix_forwards_conformance_fixes(build_folder, memory_folder):
199
+ api = FakeCodeplainAPI(unittests_fix_response={"test_app.py": "def test_add(): pass\n"})
238
200
  ctx = make_conformance_context()
239
- ctx.implementation_code_fixes.append(conformance_fix(1))
201
+ ctx.implementation_code_fixes.append(
202
+ {"hypothesis": "off by one", "approach": "add one", "code_diff": {"app.py": "+ return a + b + 1"}}
203
+ )
240
204
  render_context = make_render_context(api, build_folder, memory_folder, ctx)
241
205
 
242
206
  outcome, _ = run_unit_tests_fix(render_context)
243
207
 
244
208
  assert outcome == FixUnitTests.SUCCESSFUL_OUTCOME
245
- kind, task_params = api.agent_calls[0]
246
- assert kind == "start"
247
- assert task_params["conformance_tests_fixes"] == ctx.implementation_code_fixes
209
+ assert len(api.unittests_fix_calls) == 1
210
+ assert api.unittests_fix_calls[0]["conformance_tests_fixes"] == ctx.implementation_code_fixes
248
211
  # The forwarded list is a copy, so later conformance fixes do not mutate what was sent.
249
- assert task_params["conformance_tests_fixes"] is not ctx.implementation_code_fixes
250
- # The file the conformance fix changed is seeded although the FRID did not change it.
251
- assert "app.py" in task_params["relevant_files"]
252
- assert ctx.unit_tests_agent_session.session_id == "s1"
253
-
254
-
255
- def test_next_unit_test_loop_of_conformance_phase_continues_the_session_with_only_new_fixes(
256
- build_folder, memory_folder
257
- ):
258
- api = FakeCodeplainAPI()
259
- ctx = make_conformance_context()
260
- ctx.implementation_code_fixes.append(conformance_fix(1))
261
- render_context = make_render_context(api, build_folder, memory_folder, ctx)
262
- run_unit_tests_fix(render_context)
263
-
264
- # The fix was accepted; the conformance tests fixer changes the code again and the unit tests fail again.
265
- ctx.implementation_code_fixes.append(conformance_fix(2))
266
- start_new_unit_test_loop(render_context)
267
- run_unit_tests_fix(render_context, issue="2 failed")
212
+ assert api.unittests_fix_calls[0]["conformance_tests_fixes"] is not ctx.implementation_code_fixes
268
213
 
269
- assert api.sessions_started == 1
270
- kind, session_id, tool_results = api.agent_calls[1]
271
- assert (kind, session_id) == ("continue", "s1")
272
- submit_answer = tool_results[-1]
273
- assert submit_answer["call_id"] == "submit-1"
274
- assert submit_answer["output"].startswith("Your fix was accepted: the unit tests passed.")
275
- assert "Conformance Tests Fix below" in submit_answer["output"]
276
- assert submit_answer["conformance_tests_fixes"] == [conformance_fix(2)]
277
- assert submit_answer["test_output"] == "2 failed"
278
214
 
279
-
280
- def test_retry_within_a_unit_test_loop_says_the_fix_did_not_work(build_folder, memory_folder):
281
- api = FakeCodeplainAPI()
282
- ctx = make_conformance_context()
283
- ctx.implementation_code_fixes.append(conformance_fix(1))
284
- render_context = make_render_context(api, build_folder, memory_folder, ctx)
285
- run_unit_tests_fix(render_context)
286
-
287
- run_unit_tests_fix(render_context, issue="still failing")
288
-
289
- submit_answer = api.agent_calls[1][2][-1]
290
- assert "still fail" in submit_answer["output"]
291
- # Fix 1 was already shown to the session, so it is not sent again.
292
- assert "conformance_tests_fixes" not in submit_answer
293
-
294
-
295
- def test_new_conformance_phase_starts_a_new_session(build_folder, memory_folder):
296
- api = FakeCodeplainAPI()
297
- render_context = make_render_context(api, build_folder, memory_folder, make_conformance_context())
298
- run_unit_tests_fix(render_context)
299
-
300
- # E.g. the functionality is re-rendered from scratch: the conformance tests running context is recreated.
301
- render_context.conformance_tests_running_context = make_conformance_context()
302
- start_new_unit_test_loop(render_context)
303
- run_unit_tests_fix(render_context)
304
-
305
- assert [call[0] for call in api.agent_calls] == ["start", "start"]
306
-
307
-
308
- def test_unit_tests_outside_conformance_phase_get_no_fixes_and_a_session_per_loop(build_folder, memory_folder):
215
+ def test_unit_tests_fix_outside_conformance_phase_sends_no_fixes(build_folder, memory_folder):
309
216
  api = FakeCodeplainAPI()
310
217
  render_context = make_render_context(api, build_folder, memory_folder, None)
311
- run_unit_tests_fix(render_context)
312
- start_new_unit_test_loop(render_context)
218
+
313
219
  run_unit_tests_fix(render_context)
314
220
 
315
- assert [call[0] for call in api.agent_calls] == ["start", "start"]
316
- assert all("conformance_tests_fixes" not in call[1] for call in api.agent_calls)
221
+ assert api.unittests_fix_calls[0]["conformance_tests_fixes"] is None
317
222
 
318
223
 
319
224
  def test_unit_tests_fix_in_conformance_phase_without_implementation_changes_sends_no_fixes(build_folder, memory_folder):
@@ -322,4 +227,4 @@ def test_unit_tests_fix_in_conformance_phase_without_implementation_changes_send
322
227
 
323
228
  run_unit_tests_fix(render_context)
324
229
 
325
- assert "conformance_tests_fixes" not in api.agent_calls[0][1]
230
+ assert api.unittests_fix_calls[0]["conformance_tests_fixes"] is None
@@ -6,7 +6,7 @@ from textual.widgets import Static
6
6
 
7
7
  from event_bus import EventBus
8
8
  from plain2code_state import RunState
9
- from tui.components import ProgressItem, SubstateLine, TUIComponents
9
+ from tui.components import FRIDProgress, ProgressItem, RenderingInfoBox, SubstateLine, TUIComponents
10
10
  from tui.models import Substate
11
11
  from tui.plain2code_tui import Plain2CodeTUI
12
12
  from tui.widget_helpers import display_error_message, display_success_message, update_progress_item_substates
@@ -86,3 +86,62 @@ def test_status_messages_keep_brackets():
86
86
  assert "[Errno 2] No such file: run_tests.sh [b" in str(status.content)
87
87
 
88
88
  asyncio.run(scenario())
89
+
90
+
91
+ # A functionality quote as the render TUI receives it: several lines, square brackets included.
92
+ MULTILINE_FUNCTIONALITY_TEXT = (
93
+ "Functionality 3: :User: should be able to add a :Task: [optional]\n"
94
+ " - The :Task: must have non-empty content.\n"
95
+ " - The :Task: is appended to the end of the list."
96
+ )
97
+
98
+
99
+ def test_functionality_text_collapses_and_expands_with_ctrl_o():
100
+ async def scenario():
101
+ event_bus = EventBus()
102
+ run_state = RunState(spec_filename="x.plain")
103
+ app = _make_app(run_state, event_bus)
104
+ async with app.run_test() as pilot:
105
+ frid_progress = app.query_one(f"#{TUIComponents.FRID_PROGRESS.value}", FRIDProgress)
106
+ info_box = frid_progress.query_one(RenderingInfoBox)
107
+ info_box.update_functionality(MULTILINE_FUNCTIONALITY_TEXT)
108
+ await pilot.pause()
109
+
110
+ widget = info_box.functionality_widget
111
+ assert widget is not None
112
+
113
+ # Collapsed by default: first line only, with the expand hint.
114
+ collapsed = str(widget.content)
115
+ assert "Functionality 3: :User: should be able to add a :Task: [optional]" in collapsed
116
+ assert "non-empty content" not in collapsed
117
+ assert RenderingInfoBox.EXPAND_HINT in collapsed
118
+
119
+ await pilot.press("ctrl+o")
120
+ await pilot.pause()
121
+ expanded = str(widget.content)
122
+ assert "non-empty content" in expanded
123
+ assert "appended to the end of the list" in expanded
124
+ assert RenderingInfoBox.COLLAPSE_HINT in expanded
125
+
126
+ await pilot.press("ctrl+o")
127
+ await pilot.pause()
128
+ assert str(widget.content) == collapsed
129
+
130
+ asyncio.run(scenario())
131
+
132
+
133
+ def test_single_line_functionality_text_has_no_hint():
134
+ async def scenario():
135
+ event_bus = EventBus()
136
+ run_state = RunState(spec_filename="x.plain")
137
+ app = _make_app(run_state, event_bus)
138
+ async with app.run_test() as pilot:
139
+ info_box = app.query_one(f"#{TUIComponents.FRID_PROGRESS.value}", FRIDProgress).query_one(RenderingInfoBox)
140
+ info_box.update_functionality("Functionality 1: :User: should be able to add a :Task:")
141
+ await pilot.pause()
142
+
143
+ widget = info_box.functionality_widget
144
+ assert widget is not None
145
+ assert str(widget.content) == "Functionality 1: :User: should be able to add a :Task:"
146
+
147
+ asyncio.run(scenario())
@@ -339,10 +339,14 @@ class ProgressItem(Vertical):
339
339
  class RenderingInfoBox(Vertical):
340
340
  """Responsive container for module and functionality information."""
341
341
 
342
+ EXPAND_HINT = "(ctrl+o to expand)"
343
+ COLLAPSE_HINT = "(ctrl+o to collapse)"
344
+
342
345
  def __init__(self, **kwargs):
343
346
  super().__init__(**kwargs)
344
347
  self.module_text = ""
345
348
  self.functionality_text = ""
349
+ self.functionality_expanded = False
346
350
  self.module_widget: Static | None = None
347
351
  self.functionality_widget: Static | None = None
348
352
 
@@ -356,12 +360,31 @@ class RenderingInfoBox(Vertical):
356
360
  self.functionality_text = text
357
361
  self._refresh_content()
358
362
 
363
+ def toggle_functionality(self) -> None:
364
+ """Expand or collapse the functionality text."""
365
+ self.functionality_expanded = not self.functionality_expanded
366
+ self._refresh_content()
367
+
368
+ def _format_functionality(self) -> Content:
369
+ """Build the functionality line, collapsed to its first line unless expanded.
370
+
371
+ The functionality text comes from the spec and may contain square brackets, so it
372
+ is assembled as Content and never goes through the markup parser.
373
+ """
374
+ text = self.functionality_text or ""
375
+ lines = text.splitlines()
376
+ if len(lines) <= 1:
377
+ return Content(text)
378
+ if self.functionality_expanded:
379
+ return Content.assemble(f"{text}\n", (self.COLLAPSE_HINT, "#888888"))
380
+ return Content.assemble(f"{lines[0]} \u2026 ", (self.EXPAND_HINT, "#888888"))
381
+
359
382
  def _refresh_content(self) -> None:
360
383
  """Refresh text inside the box."""
361
384
  if self.module_widget is not None:
362
385
  self.module_widget.update(self.module_text or "")
363
386
  if self.functionality_widget is not None:
364
- self.functionality_widget.update(self.functionality_text or "")
387
+ self.functionality_widget.update(self._format_functionality())
365
388
 
366
389
  def on_mount(self) -> None:
367
390
  """Initialize default labels on mount."""
@@ -372,7 +395,7 @@ class RenderingInfoBox(Vertical):
372
395
 
373
396
  def compose(self):
374
397
  self.module_widget = Static(self.module_text, classes="rendering-info-row", markup=False)
375
- self.functionality_widget = Static(self.functionality_text, classes="rendering-info-row", markup=False)
398
+ self.functionality_widget = Static(self._format_functionality(), classes="rendering-info-row", markup=False)
376
399
  yield Static("module status", classes="rendering-info-title")
377
400
  with Vertical(classes="rendering-info-box"):
378
401
  yield self.module_widget
@@ -65,6 +65,7 @@ class Plain2CodeTUI(App):
65
65
  Binding("ctrl+d", "quit", "Quit", show=False),
66
66
  Binding("enter", "enter_exit", "Exit", show=False),
67
67
  Binding("ctrl+p", "pause", "Pause", show=False, priority=True),
68
+ Binding("ctrl+o", "toggle_functionality", "Expand/Collapse", show=False),
68
69
  ("ctrl+l", "toggle_logs", "Toggle Logs"),
69
70
  ]
70
71
 
@@ -231,6 +232,14 @@ class Plain2CodeTUI(App):
231
232
  self.run_state.render_time_accumulated,
232
233
  )
233
234
 
235
+ def action_toggle_functionality(self) -> None:
236
+ """Expand or collapse the functionality text in the rendering info box."""
237
+ try:
238
+ frid_progress = self.query_one(f"#{TUIComponents.FRID_PROGRESS.value}", FRIDProgress)
239
+ frid_progress.query_one(RenderingInfoBox).toggle_functionality()
240
+ except NoMatches:
241
+ pass
242
+
234
243
  def action_toggle_logs(self) -> None:
235
244
  """Toggle between dashboard and log view."""
236
245
  switcher = self.query_one(f"#{TUIComponents.CONTENT_SWITCHER.value}", ContentSwitcher)