codeplain 0.3.10.dev3__tar.gz → 0.3.10.dev5__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 (265) hide show
  1. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/PKG-INFO +1 -1
  2. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/codeplain_REST_api.py +6 -14
  3. codeplain-0.3.10.dev5/conformance_fix_journal.py +207 -0
  4. codeplain-0.3.10.dev5/memory_management.py +52 -0
  5. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/plain2code.py +15 -0
  6. codeplain-0.3.10.dev5/render_machine/actions/distill_conformance_test_memory.py +56 -0
  7. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/actions/fix_conformance_test.py +58 -29
  8. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/actions/prepare_repositories.py +8 -0
  9. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/actions/run_conformance_tests.py +5 -11
  10. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/render_types.py +0 -4
  11. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/state_machine_config.py +10 -1
  12. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/states.py +1 -0
  13. codeplain-0.3.10.dev5/tests/test_conformance_fix_journal.py +259 -0
  14. codeplain-0.3.10.dev5/tests/test_distill_conformance_test_memory.py +145 -0
  15. codeplain-0.3.10.dev5/tests/test_dry_run_link_validation.py +60 -0
  16. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_prepare_repositories_layout.py +38 -0
  17. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tui/state_handlers.py +7 -1
  18. codeplain-0.3.10.dev3/memory_management.py +0 -132
  19. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/.claude/settings.json +0 -0
  20. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/.flake8 +0 -0
  21. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/.github/dependabot.yml +0 -0
  22. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/.github/workflows/e2e.yml +0 -0
  23. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/.github/workflows/lint-and-test.yml +0 -0
  24. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/.github/workflows/lint-powershell.yml +0 -0
  25. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/.github/workflows/nofity-slack-on-main-merge.yml +0 -0
  26. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/.github/workflows/publish-dev-to-pypi.yml +0 -0
  27. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/.github/workflows/publish-install-script.yml +0 -0
  28. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/.github/workflows/publish-to-pypi.yml +0 -0
  29. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/.gitignore +0 -0
  30. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/.vscode/launch.json +0 -0
  31. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/.vscode/settings.json +0 -0
  32. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/CLAUDE.md +0 -0
  33. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/CONTRIBUTING.md +0 -0
  34. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/LICENSE +0 -0
  35. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/README.md +0 -0
  36. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/change_detection.py +0 -0
  37. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/cli_output/__init__.py +0 -0
  38. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/cli_output/dry_run.py +0 -0
  39. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/cli_output/render_summary.py +0 -0
  40. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/cli_output/status.py +0 -0
  41. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/concept_utils.py +0 -0
  42. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/config/__init__.py +0 -0
  43. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/config/system_config.yaml +0 -0
  44. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/diff_utils.py +0 -0
  45. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/docs/generate_cli.py +0 -0
  46. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/docs/plain2code_cli.md +0 -0
  47. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/docs/starting_a_plain_project_from_scratch.md +0 -0
  48. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/event_bus.py +0 -0
  49. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/examples/example_hello_world_golang/config.yaml +0 -0
  50. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/examples/example_hello_world_golang/harness_tests/hello_world_test.go +0 -0
  51. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/examples/example_hello_world_golang/hello_world_golang.plain +0 -0
  52. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/examples/example_hello_world_golang/run.sh +0 -0
  53. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/examples/example_hello_world_python/config.yaml +0 -0
  54. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/examples/example_hello_world_python/harness_tests/hello_world_display/test_hello_world.py +0 -0
  55. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/examples/example_hello_world_python/hello_world_python.plain +0 -0
  56. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/examples/example_hello_world_python/run.sh +0 -0
  57. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/examples/example_hello_world_react/config.yaml +0 -0
  58. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress/e2e/hello_world.cy.ts +0 -0
  59. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress/support/e2e.ts +0 -0
  60. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress.config.ts +0 -0
  61. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/examples/example_hello_world_react/harness_tests/hello_world_display/package.json +0 -0
  62. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/examples/example_hello_world_react/harness_tests/hello_world_display/tsconfig.json +0 -0
  63. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/examples/example_hello_world_react/hello_world_react.plain +0 -0
  64. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/examples/example_hello_world_react/run.sh +0 -0
  65. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/examples/run.sh +0 -0
  66. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/file_utils.py +0 -0
  67. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/git_utils.py +0 -0
  68. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/install/bash/examples.sh +0 -0
  69. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/install/bash/install.sh +0 -0
  70. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/install/bash/walkthrough.sh +0 -0
  71. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/install/powershell/examples.ps1 +0 -0
  72. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/install/powershell/install.ps1 +0 -0
  73. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/install/powershell/walkthrough.ps1 +0 -0
  74. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/metadata_utils.py +0 -0
  75. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/module_renderer.py +0 -0
  76. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/partial_rendering.py +0 -0
  77. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/path_resolution.py +0 -0
  78. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/plain2code_arguments.py +0 -0
  79. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/plain2code_cli.md +0 -0
  80. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/plain2code_console.py +0 -0
  81. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/plain2code_events.py +0 -0
  82. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/plain2code_exceptions.py +0 -0
  83. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/plain2code_logger.py +0 -0
  84. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/plain2code_nodes.py +0 -0
  85. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/plain2code_read_config.py +0 -0
  86. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/plain2code_state.py +0 -0
  87. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/plain2code_telemetry.py +0 -0
  88. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/plain2code_utils.py +0 -0
  89. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/plain_file.py +0 -0
  90. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/plain_modules.py +0 -0
  91. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/plain_spec.py +0 -0
  92. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/pyproject.toml +0 -0
  93. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/pytest.ini +0 -0
  94. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/__init__.py +0 -0
  95. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/actions/analyze_specification_ambiguity.py +0 -0
  96. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/actions/base_action.py +0 -0
  97. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/actions/commit_conformance_tests_changes.py +0 -0
  98. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/actions/commit_implementation_code_changes.py +0 -0
  99. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/actions/create_dist.py +0 -0
  100. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/actions/exit_with_error.py +0 -0
  101. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/actions/finish_functional_requirement.py +0 -0
  102. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/actions/fix_unit_tests.py +0 -0
  103. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/actions/prepare_testing_environment.py +0 -0
  104. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/actions/refactor_code.py +0 -0
  105. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/actions/render_conformance_tests.py +0 -0
  106. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/actions/render_functional_requirement.py +0 -0
  107. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/actions/run_unit_tests.py +0 -0
  108. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/actions/summarize_conformance_tests.py +0 -0
  109. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/code_renderer.py +0 -0
  110. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/conformance_tests.py +0 -0
  111. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/implementation_code_helpers.py +0 -0
  112. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/render_context.py +0 -0
  113. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/render_utils.py +0 -0
  114. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/render_machine/triggers.py +0 -0
  115. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/requirements.txt +0 -0
  116. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/resources/codeplain_overview.png +0 -0
  117. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/resources/plain_example.png +0 -0
  118. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/standard_template_library/golang-console-app-template.plain +0 -0
  119. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/standard_template_library/python-console-app-template.plain +0 -0
  120. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/standard_template_library/typescript-react-app-boilerplate.plain +0 -0
  121. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/standard_template_library/typescript-react-app-template.plain +0 -0
  122. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/system_config.py +0 -0
  123. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/test_scripts/run_conformance_tests_cypress.ps1 +0 -0
  124. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/test_scripts/run_conformance_tests_cypress.sh +0 -0
  125. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/test_scripts/run_conformance_tests_golang.ps1 +0 -0
  126. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/test_scripts/run_conformance_tests_golang.sh +0 -0
  127. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/test_scripts/run_conformance_tests_python.ps1 +0 -0
  128. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/test_scripts/run_conformance_tests_python.sh +0 -0
  129. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/test_scripts/run_unittests_flutter.ps1 +0 -0
  130. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/test_scripts/run_unittests_flutter.sh +0 -0
  131. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/test_scripts/run_unittests_golang.ps1 +0 -0
  132. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/test_scripts/run_unittests_golang.sh +0 -0
  133. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/test_scripts/run_unittests_python.ps1 +0 -0
  134. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/test_scripts/run_unittests_python.sh +0 -0
  135. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/test_scripts/run_unittests_react.ps1 +0 -0
  136. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/test_scripts/run_unittests_react.sh +0 -0
  137. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/__init__.py +0 -0
  138. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/conftest.py +0 -0
  139. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/acceptance_tests_warning/main_requiring_acceptance_tests.plain +0 -0
  140. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/acceptance_tests_warning/required_with_acceptance_tests.plain +0 -0
  141. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/imports/circular_imports_1.plain +0 -0
  142. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/imports/circular_imports_2.plain +0 -0
  143. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/imports/circular_imports_main.plain +0 -0
  144. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/imports/diamond_import_1.plain +0 -0
  145. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/imports/diamond_import_2.plain +0 -0
  146. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/imports/diamond_import_common.plain +0 -0
  147. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/imports/diamond_imports_main.plain +0 -0
  148. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/imports/import_with_functionalities.plain +0 -0
  149. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/imports/imports_module_with_functionalities.plain +0 -0
  150. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/imports/non_existent_import.plain +0 -0
  151. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/partial_rendering/pr_code_var.plain +0 -0
  152. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/partial_rendering/pr_implement.plain +0 -0
  153. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/partial_rendering/pr_leaf.plain +0 -0
  154. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/partial_rendering/pr_middle.plain +0 -0
  155. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/partial_rendering/pr_root.plain +0 -0
  156. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/partial_rendering/pr_solo.plain +0 -0
  157. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfile/add_new_task_modal_specification.yaml +0 -0
  158. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfile/duplicate_specification_heading.plain +0 -0
  159. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfile/empty_functional_specs_section.plain +0 -0
  160. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfile/invalid_specification_order.plain +0 -0
  161. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfile/missing_non_functional_requirements.plain +0 -0
  162. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfile/no_functional_specs_section.plain +0 -0
  163. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfile/plain_source_with_absolute_link.plain +0 -0
  164. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfile/plain_source_with_url_link.plain +0 -0
  165. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfile/task_list_ui_specification.yaml +0 -0
  166. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfile/task_manager_with_reference_links.plain +0 -0
  167. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfile/without_non_functional_requirement.plain +0 -0
  168. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/concept_validation_acceptance_tests.plain +0 -0
  169. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/concept_validation_acceptance_tests_nondefined.plain +0 -0
  170. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/concept_validation_defined_nondefined.plain +0 -0
  171. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/concept_validation_defined_nondefined_2.plain +0 -0
  172. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/concept_validation_definition.plain +0 -0
  173. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/concept_validation_noconcepts.plain +0 -0
  174. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/concept_validation_nonconcept.plain +0 -0
  175. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/concept_validation_nondefined.plain +0 -0
  176. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/concept_validation_redefinition.plain +0 -0
  177. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/concept_validation_several_concepts.plain +0 -0
  178. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/concept_validation_valid.plain +0 -0
  179. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/exported_concepts.plain +0 -0
  180. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/exported_concepts_base.plain +0 -0
  181. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/exported_concepts_example.plain +0 -0
  182. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/exported_concepts_missing.plain +0 -0
  183. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/exported_concepts_missing_example.plain +0 -0
  184. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/exported_concepts_nested.plain +0 -0
  185. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/exported_concepts_nested_example.plain +0 -0
  186. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/exported_concepts_transitive_example.plain +0 -0
  187. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/exported_concepts_transitive_l1.plain +0 -0
  188. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/exported_concepts_transitive_l2.plain +0 -0
  189. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/plain_file_parser_with_comments.plain +0 -0
  190. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/plain_file_with_comments_indented.plain +0 -0
  191. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/regular_plain_source.plain +0 -0
  192. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/required_concepts.plain +0 -0
  193. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/required_concepts_defs.plain +0 -0
  194. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/required_concepts_example.plain +0 -0
  195. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/required_concepts_l1.plain +0 -0
  196. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/required_concepts_l2.plain +0 -0
  197. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/required_concepts_missing.plain +0 -0
  198. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/required_concepts_module.plain +0 -0
  199. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/required_concepts_partial.plain +0 -0
  200. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/required_concepts_partial_duplicate.plain +0 -0
  201. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/topological_sort.plain +0 -0
  202. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/plainfileparser/topological_sort_not_referenced.plain +0 -0
  203. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/requires/circular_requires_main.plain +0 -0
  204. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/requires/circular_requires_sub.plain +0 -0
  205. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/requires/diamond_requires_1.plain +0 -0
  206. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/requires/diamond_requires_2.plain +0 -0
  207. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/requires/diamond_requires_common.plain +0 -0
  208. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/requires/diamond_requires_main.plain +0 -0
  209. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/requires/independent_requires_1.plain +0 -0
  210. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/requires/independent_requires_2.plain +0 -0
  211. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/requires/independent_requires_main.plain +0 -0
  212. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/requires/non_existent_require.plain +0 -0
  213. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/requires/normal_requires_1.plain +0 -0
  214. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/requires/normal_requires_2.plain +0 -0
  215. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/requires/normal_requires_common.plain +0 -0
  216. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/requires/normal_requires_main.plain +0 -0
  217. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/sample_base64_image.txt +0 -0
  218. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/simple.plain +0 -0
  219. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/templates/block_level_include.plain +0 -0
  220. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/templates/code_variables.plain +0 -0
  221. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/templates/header.plain +0 -0
  222. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/templates/implement.plain +0 -0
  223. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/templates/implement_2.plain +0 -0
  224. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/templates/template_include.plain +0 -0
  225. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/data/templates/test_hardest_problem.plain +0 -0
  226. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/e2e/Dockerfile +0 -0
  227. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/e2e/conftest.py +0 -0
  228. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/e2e/test_hello_world_python.py +0 -0
  229. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/e2e/test_hello_world_python_windows.py +0 -0
  230. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_arg_provenance.py +0 -0
  231. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_change_detection.py +0 -0
  232. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_cli_output.py +0 -0
  233. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_console_log_styles.py +0 -0
  234. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_file_utils.py +0 -0
  235. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_folder_path_resolution.py +0 -0
  236. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_git_utils.py +0 -0
  237. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_headless_logging.py +0 -0
  238. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_imports.py +0 -0
  239. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_log_file_name_resolution.py +0 -0
  240. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_logging_config_path_resolution.py +0 -0
  241. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_partial_rendering.py +0 -0
  242. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_path_resolution.py +0 -0
  243. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_plain2code.py +0 -0
  244. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_plain2code_state.py +0 -0
  245. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_plain2code_utils.py +0 -0
  246. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_plain_modules.py +0 -0
  247. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_plainfile.py +0 -0
  248. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_plainfileparser.py +0 -0
  249. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_plainspec.py +0 -0
  250. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_requires.py +0 -0
  251. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_resolve_config_file.py +0 -0
  252. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_script_path_resolution.py +0 -0
  253. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_system_config.py +0 -0
  254. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_telemetry.py +0 -0
  255. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_tui_usage.py +0 -0
  256. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tests/test_usage_summary.py +0 -0
  257. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tui/__init__.py +0 -0
  258. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tui/components.py +0 -0
  259. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tui/models.py +0 -0
  260. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tui/plain2code_tui.py +0 -0
  261. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tui/plain_module_render_choice_tui.py +0 -0
  262. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tui/spinner.py +0 -0
  263. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tui/styles.css +0 -0
  264. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/tui/widget_helpers.py +0 -0
  265. {codeplain-0.3.10.dev3 → codeplain-0.3.10.dev5}/usage_summary.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: codeplain
3
- Version: 0.3.10.dev3
3
+ Version: 0.3.10.dev5
4
4
  Summary: Transform plain language specifications into working code
5
5
  License-File: LICENSE
6
6
  Classifier: Environment :: Console
@@ -254,7 +254,7 @@ class CodeplainAPI:
254
254
 
255
255
  return self.post_request(endpoint_url, headers, payload, run_state)
256
256
 
257
- def create_conformance_test_memory(
257
+ def distill_conformance_test_memory(
258
258
  self,
259
259
  frid,
260
260
  plain_source_tree,
@@ -263,15 +263,10 @@ class CodeplainAPI:
263
263
  memory_files_content,
264
264
  module_name,
265
265
  required_modules,
266
- code_diff,
267
- conformance_tests_files,
268
- acceptance_tests,
269
- conformance_tests_issue,
270
- conformance_tests_folder_name,
271
- previous_conformance_tests_issue_old,
266
+ fix_journals,
272
267
  run_state: RunState,
273
268
  ):
274
- endpoint_url = f"{self.api_url}/create_conformance_test_memory"
269
+ endpoint_url = f"{self.api_url}/distill_conformance_test_memory"
275
270
  headers = {"X-API-Key": self.api_key, "Content-Type": "application/json"}
276
271
 
277
272
  payload = {
@@ -282,12 +277,7 @@ class CodeplainAPI:
282
277
  "memory_files_content": memory_files_content,
283
278
  "module_name": module_name,
284
279
  "required_modules": required_modules,
285
- "code_diff": code_diff,
286
- "conformance_tests_files": conformance_tests_files,
287
- "acceptance_tests": acceptance_tests,
288
- "conformance_tests_issue": conformance_tests_issue,
289
- "conformance_tests_folder_name": conformance_tests_folder_name,
290
- "previous_conformance_tests_issue": previous_conformance_tests_issue_old,
280
+ "fix_journals": fix_journals,
291
281
  }
292
282
 
293
283
  return self.post_request(endpoint_url, headers, payload, run_state)
@@ -381,6 +371,7 @@ class CodeplainAPI:
381
371
  conformance_tests_folder_name,
382
372
  current_testing_frid_high_level_implementation_plan: Optional[str],
383
373
  conflicting_requirements_count: int,
374
+ fix_attempts_journal: list,
384
375
  run_state: RunState,
385
376
  ):
386
377
  endpoint_url = f"{self.api_url}/fix_conformance_tests_issue"
@@ -403,6 +394,7 @@ class CodeplainAPI:
403
394
  "conformance_tests_folder_name": conformance_tests_folder_name,
404
395
  "current_testing_frid_high_level_implementation_plan": current_testing_frid_high_level_implementation_plan,
405
396
  "conflicting_requirements_count": conflicting_requirements_count,
397
+ "fix_attempts_journal": fix_attempts_journal,
406
398
  }
407
399
 
408
400
  if acceptance_tests is not None:
@@ -0,0 +1,207 @@
1
+ """Deterministic journal of conformance tests fix attempts.
2
+
3
+ While a functionality's conformance tests are failing, every fix attempt is recorded here: the
4
+ issue that triggered it, what the fixer said it tried, which files it changed and whether the tests
5
+ passed afterwards. A digest of the journal is sent back with every fix request so the fixer does
6
+ not repeat attempts that already failed, and once the functionality's conformance tests all pass
7
+ the journal is distilled into durable memories and deleted.
8
+
9
+ Each entry carries the issue in two forms: the raw script output (kept untruncated, on disk only)
10
+ and the prepared form the server built for the fix prompt (trimmed, and summarized or truncated
11
+ when oversized) - the issue exactly the way the fixer saw it. The prepared form is what travels in
12
+ the fix digest and the distillation payload; the raw form exists for humans debugging a render.
13
+ An attempt's outcome text is not stored twice: the output produced after an attempt is the raw
14
+ issue of the next attempt.
15
+
16
+ Journals are keyed by the (module, frid) whose tests are being fixed - during the regression sweep
17
+ that can be a previously implemented functionality, possibly from a required module.
18
+ """
19
+
20
+ import hashlib
21
+ import json
22
+ import os
23
+ import shutil
24
+
25
+ from plain2code_console import console
26
+
27
+ CONFORMANCE_TEST_JOURNAL_SUBFOLDER = "conformance_test_journal"
28
+
29
+ # Cap for the issue excerpts included in payloads (the per-attempt issue in the fix digest, and the
30
+ # initial issue in the distillation payload). The server-prepared issue is already bounded (LLM
31
+ # summary or ~10k-char truncation), so this only trims the rare oversized case - it must not
32
+ # routinely cut into summaries the server already sized for a prompt.
33
+ MAX_ISSUE_EXCERPT_CHARS = 8000
34
+
35
+ # The keys of a journal entry that make up the digest sent to the fixer and the distiller.
36
+ DIGEST_KEYS = ["attempt", "hypothesis", "approach", "target", "files_changed", "duplicate_of", "result"]
37
+
38
+
39
+ def _trim_issue(issue: str | None, max_chars: int) -> str | None:
40
+ if issue is None or len(issue) <= max_chars:
41
+ return issue
42
+ return f"(TRUNCATED - showing the last {max_chars} characters)\n" + issue[-max_chars:]
43
+
44
+
45
+ def normalized_diff_hash(diff_files: dict[str, str] | None) -> str | None:
46
+ """Hashes a fix's diff so that essentially identical retries can be detected.
47
+
48
+ Whitespace-only differences are ignored - a fix that reproduces an earlier failed change with
49
+ different formatting is still the same attempt.
50
+ """
51
+ if not diff_files:
52
+ return None
53
+
54
+ normalized_parts = []
55
+ for file_name in sorted(diff_files):
56
+ content_lines = [line.strip() for line in (diff_files[file_name] or "").splitlines() if line.strip()]
57
+ normalized_parts.append(file_name + "\n" + "\n".join(content_lines))
58
+
59
+ return hashlib.sha256("\n".join(normalized_parts).encode("utf-8")).hexdigest()
60
+
61
+
62
+ class FixAttemptJournal:
63
+ """File-backed journal, one JSONL file per (module, frid) whose conformance tests are fixed."""
64
+
65
+ def __init__(self, memory_folder: str):
66
+ self.journal_folder = os.path.join(memory_folder, CONFORMANCE_TEST_JOURNAL_SUBFOLDER)
67
+
68
+ def _journal_path(self, module_name: str, frid: str) -> str:
69
+ return os.path.join(self.journal_folder, f"{module_name}__{frid}.jsonl")
70
+
71
+ def _read_entries(self, module_name: str, frid: str) -> list[dict]:
72
+ journal_path = self._journal_path(module_name, frid)
73
+ if not os.path.exists(journal_path):
74
+ return []
75
+
76
+ entries = []
77
+ with open(journal_path, "r", encoding="utf-8") as journal_file:
78
+ for line in journal_file:
79
+ line = line.strip()
80
+ if not line:
81
+ continue
82
+ try:
83
+ entries.append(json.loads(line))
84
+ except json.JSONDecodeError:
85
+ console.error(f"Skipping malformed journal entry in {journal_path}.")
86
+
87
+ return entries
88
+
89
+ def _write_entries(self, module_name: str, frid: str, entries: list[dict]):
90
+ os.makedirs(self.journal_folder, exist_ok=True)
91
+ with open(self._journal_path(module_name, frid), "w", encoding="utf-8") as journal_file:
92
+ for entry in entries:
93
+ journal_file.write(json.dumps(entry) + "\n")
94
+
95
+ def open_attempt(self, module_name: str, frid: str, attempt_no: int, issue_before: str):
96
+ """Starts a new journal entry for a fix attempt that is about to be made.
97
+
98
+ The raw issue is stored untruncated - it never travels in a payload, only its prepared form
99
+ (recorded later by record_fix) does.
100
+ """
101
+ entries = self._read_entries(module_name, frid)
102
+ entries.append({"attempt": attempt_no, "issue_before_raw": issue_before})
103
+ self._write_entries(module_name, frid, entries)
104
+
105
+ def record_fix(
106
+ self,
107
+ module_name: str,
108
+ frid: str,
109
+ fix_attempt_summary: dict | None,
110
+ files_changed: list[str],
111
+ target: str,
112
+ diff_files: dict[str, str] | None,
113
+ prepared_issue: str | None,
114
+ ):
115
+ """Completes the open attempt with what the fix actually did.
116
+
117
+ The prepared issue is the form the server built for the fix prompt - the issue exactly the
118
+ way the fixer saw it. The diff hash marks the attempt as a duplicate when an earlier attempt
119
+ in the same journal produced essentially the same change - the strongest signal that the
120
+ fixer is going in circles.
121
+ """
122
+ entries = self._read_entries(module_name, frid)
123
+ if not entries:
124
+ console.error("Cannot record a fix - no journal entry is open.")
125
+ return
126
+
127
+ entry = entries[-1]
128
+ if isinstance(fix_attempt_summary, dict):
129
+ entry["hypothesis"] = fix_attempt_summary.get("hypothesis")
130
+ entry["approach"] = fix_attempt_summary.get("approach")
131
+ if prepared_issue:
132
+ entry["issue_before"] = prepared_issue
133
+ entry["files_changed"] = sorted(files_changed)
134
+ entry["target"] = target
135
+
136
+ diff_hash = normalized_diff_hash(diff_files)
137
+ entry["diff_hash"] = diff_hash
138
+ if diff_hash is not None:
139
+ for earlier_entry in entries[:-1]:
140
+ if earlier_entry.get("diff_hash") == diff_hash:
141
+ entry["duplicate_of"] = earlier_entry["attempt"]
142
+ break
143
+
144
+ self._write_entries(module_name, frid, entries)
145
+
146
+ def record_result(self, module_name: str, frid: str, passed: bool):
147
+ """Records whether the conformance tests passed after the last fix attempt was applied.
148
+
149
+ A failed run's output is not stored here - it becomes the raw issue of the next attempt.
150
+ Does nothing when no attempt is pending: the first test run of a functionality has no fix
151
+ attempt behind it.
152
+ """
153
+ entries = self._read_entries(module_name, frid)
154
+ if not entries or "result" in entries[-1]:
155
+ return
156
+
157
+ entries[-1]["result"] = "conformance tests passed" if passed else "conformance tests still failed"
158
+ self._write_entries(module_name, frid, entries)
159
+
160
+ def build_digest(self, module_name: str, frid: str) -> list[dict]:
161
+ """Builds the compact attempt history sent with a fix request.
162
+
163
+ Only attempts whose fix has been recorded are included - the entry just opened for the
164
+ attempt being made (and any dangling entry an interrupted render left behind) carries no
165
+ information for the fixer. Each included attempt carries the prepared issue it addressed,
166
+ so the fixer can see how the issue evolved across attempts. Raw outputs never travel - the
167
+ current issue is sent with the request separately.
168
+ """
169
+ return [self._digest_entry(entry) for entry in self._read_entries(module_name, frid) if "target" in entry]
170
+
171
+ @staticmethod
172
+ def _digest_entry(entry: dict) -> dict:
173
+ digest_entry = {key: entry[key] for key in DIGEST_KEYS if key in entry}
174
+ if entry.get("issue_before"):
175
+ digest_entry["issue"] = _trim_issue(entry["issue_before"], MAX_ISSUE_EXCERPT_CHARS)
176
+ return digest_entry
177
+
178
+ def collect_all(self) -> list[dict]:
179
+ """Collects the digests of every journal, keyed by the tested module and frid, for distillation."""
180
+ if not os.path.exists(self.journal_folder):
181
+ return []
182
+
183
+ journals = []
184
+ for file_name in sorted(os.listdir(self.journal_folder)):
185
+ if not file_name.endswith(".jsonl") or "__" not in file_name:
186
+ continue
187
+ module_name, _, frid = file_name[: -len(".jsonl")].rpartition("__")
188
+ entries = [entry for entry in self._read_entries(module_name, frid) if "target" in entry]
189
+ if not entries:
190
+ continue
191
+ initial_issue = entries[0].get("issue_before") or _trim_issue(
192
+ entries[0].get("issue_before_raw"), MAX_ISSUE_EXCERPT_CHARS
193
+ )
194
+ journal = {
195
+ "module": module_name,
196
+ "frid": frid,
197
+ "initial_issue": initial_issue,
198
+ "attempts": [self._digest_entry(entry) for entry in entries],
199
+ }
200
+ journals.append(journal)
201
+
202
+ return journals
203
+
204
+ def clear_all(self):
205
+ """Deletes every journal. Called after the journals have been distilled into memories."""
206
+ if os.path.exists(self.journal_folder):
207
+ shutil.rmtree(self.journal_folder)
@@ -0,0 +1,52 @@
1
+ import os
2
+
3
+ import file_utils
4
+ from conformance_fix_journal import FixAttemptJournal
5
+ from plain2code_console import console
6
+
7
+ CONFORMANCE_TEST_MEMORY_SUBFOLDER = "conformance_test_memory"
8
+
9
+
10
+ class MemoryManager:
11
+ """Manages a module's memory store at <build>/<module>/.memory.
12
+
13
+ The distilled memories are inherited by the next module in the chain (copied over when its
14
+ render starts, like the code repo), so learnings travel across modules.
15
+ """
16
+
17
+ @staticmethod
18
+ def fetch_memory_files(memory_folder: str) -> tuple[list[str], dict[str, str]]:
19
+ """Fetch memory files from memory_folder/conformance_test_memory."""
20
+ memory_path = os.path.join(memory_folder, CONFORMANCE_TEST_MEMORY_SUBFOLDER)
21
+ if not os.path.exists(memory_path):
22
+ return [], {}
23
+ memory_files = file_utils.list_all_text_files(memory_path)
24
+ memory_files_content = file_utils.get_existing_files_content(memory_path, memory_files)
25
+ console.debug(f"Loaded {len(memory_files_content)} memory files.")
26
+ return memory_files, memory_files_content
27
+
28
+ def __init__(self, codeplain_api, memory_folder: str):
29
+ self.codeplain_api = codeplain_api
30
+ self.memory_folder = memory_folder
31
+ self.journal = FixAttemptJournal(memory_folder)
32
+
33
+ def store_memory_files(self, response_files: dict[str, str]):
34
+ """Stores distilled memory files, applying deletions (None content) to existing memories."""
35
+ memory_path = os.path.join(self.memory_folder, CONFORMANCE_TEST_MEMORY_SUBFOLDER)
36
+ memory_files, _ = MemoryManager.fetch_memory_files(self.memory_folder)
37
+ file_utils.store_response_files(memory_path, response_files, memory_files)
38
+
39
+ @staticmethod
40
+ def inherit_memories(source_memory_folder: str, destination_memory_folder: str):
41
+ """Copies the distilled memories of a previously rendered module into this module's store.
42
+
43
+ Only the distilled memories travel - fix attempt journals are transient state of one
44
+ module's render. A previous module without memories (e.g. shipped as an archive that
45
+ carries only code) is a no-op.
46
+ """
47
+ source_path = os.path.join(source_memory_folder, CONFORMANCE_TEST_MEMORY_SUBFOLDER)
48
+ if not os.path.isdir(source_path):
49
+ return
50
+ file_utils.copy_folder_content(
51
+ source_path, os.path.join(destination_memory_folder, CONFORMANCE_TEST_MEMORY_SUBFOLDER)
52
+ )
@@ -203,6 +203,15 @@ def warn_if_acceptance_tests_without_conformance_script(plain_module, args) -> N
203
203
  )
204
204
 
205
205
 
206
+ def validate_linked_resources(plain_module) -> None:
207
+ """Load every linked resource exactly the way rendering does, so dry-run
208
+ surfaces the same errors (binary file, missing file, oversized base64 blob)."""
209
+ for module in plain_module.all_required_modules + [plain_module]:
210
+ resources_list: list[dict] = []
211
+ plain_spec.collect_linked_resources(module.plain_source, resources_list, None, True)
212
+ file_utils.load_linked_resources(module.template_dirs, resources_list, module.module_name)
213
+
214
+
206
215
  def render( # noqa: C901
207
216
  plain_module: plain_modules.PlainModule,
208
217
  args,
@@ -393,6 +402,12 @@ def main(): # noqa: C901
393
402
  return
394
403
 
395
404
  if args.dry_run:
405
+ try:
406
+ validate_linked_resources(plain_module)
407
+ except Exception as e:
408
+ console.error(f"Error: {str(e)}")
409
+ return
410
+
396
411
  console.info("Printing dry run output...\n")
397
412
  render_range = plain_spec.compute_render_range(args, plain_module.plain_source)
398
413
  print_dry_run_output(plain_module.plain_source, render_range)
@@ -0,0 +1,56 @@
1
+ from typing import Any
2
+
3
+ from plain2code_console import console
4
+ from render_machine.actions.base_action import BaseAction
5
+ from render_machine.implementation_code_helpers import ImplementationCodeHelpers
6
+ from render_machine.render_context import RenderContext
7
+
8
+
9
+ class DistillConformanceTestMemory(BaseAction):
10
+ """Distills the conformance tests fix journals into durable memories once all tests pass.
11
+
12
+ Runs exactly once per functional requirement, as the first postprocessing step. When no fix
13
+ attempts were needed there is nothing to distill and no API call is made. Distillation is best
14
+ effort - a failure must never fail the render, so the journals are kept for the next
15
+ distillation instead.
16
+
17
+ The memories land in the module's own memory store and are inherited by the next module in the
18
+ chain when its render starts.
19
+ """
20
+
21
+ SUCCESSFUL_OUTCOME = "conformance_test_memory_distilled"
22
+
23
+ def execute(self, render_context: RenderContext, _previous_action_payload: Any | None):
24
+ journal = render_context.memory_manager.journal
25
+ fix_journals = journal.collect_all()
26
+ if not fix_journals:
27
+ return self.SUCCESSFUL_OUTCOME, None
28
+
29
+ console.info(f"Distilling conformance test fix learnings for functionality {render_context.frid_context.frid}.")
30
+
31
+ try:
32
+ _, existing_files_content = ImplementationCodeHelpers.fetch_existing_files(render_context.build_folder)
33
+ _, memory_files_content = render_context.memory_manager.fetch_memory_files(
34
+ render_context.memory_manager.memory_folder
35
+ )
36
+
37
+ response_files = render_context.codeplain_api.distill_conformance_test_memory(
38
+ frid=render_context.frid_context.frid,
39
+ plain_source_tree=render_context.plain_source_tree,
40
+ linked_resources=render_context.frid_context.linked_resources,
41
+ existing_files_content=existing_files_content,
42
+ memory_files_content=memory_files_content,
43
+ module_name=render_context.module_name,
44
+ required_modules=render_context.get_required_modules_functionalities(),
45
+ fix_journals=fix_journals,
46
+ run_state=render_context.run_state,
47
+ )
48
+
49
+ if response_files:
50
+ render_context.memory_manager.store_memory_files(response_files)
51
+ except Exception as e:
52
+ console.error(f"Failed to distill conformance test memory (keeping the journals): {e}")
53
+ return self.SUCCESSFUL_OUTCOME, None
54
+
55
+ journal.clear_all()
56
+ return self.SUCCESSFUL_OUTCOME, None
@@ -54,14 +54,13 @@ class FixConformanceTest(BaseAction):
54
54
  )
55
55
  previous_conformance_tests_issue = previous_action_payload["previous_conformance_tests_issue"]
56
56
 
57
- render_context.conformance_tests_running_context.previous_conformance_tests_issue_old = (
58
- previous_conformance_tests_issue
59
- )
60
- render_context.conformance_tests_running_context.previous_conformance_tests_issue_frid = (
61
- render_context.conformance_tests_running_context.current_testing_frid
62
- )
63
- render_context.conformance_tests_running_context.previous_conformance_tests_issue_module = (
64
- render_context.conformance_tests_running_context.current_testing_module_name
57
+ journal = render_context.memory_manager.journal
58
+ journal.record_result(ctx.current_testing_module_name, ctx.current_testing_frid, passed=False)
59
+ journal.open_attempt(
60
+ ctx.current_testing_module_name,
61
+ ctx.current_testing_frid,
62
+ ctx.fix_attempts,
63
+ previous_conformance_tests_issue,
65
64
  )
66
65
 
67
66
  existing_files, existing_files_content = ImplementationCodeHelpers.fetch_existing_files(
@@ -114,25 +113,28 @@ class FixConformanceTest(BaseAction):
114
113
  style=console.INPUT_STYLE,
115
114
  )
116
115
 
117
- [issue_reason_code, response_files] = render_context.codeplain_api.fix_conformance_tests_issue(
118
- render_context.frid_context.frid,
119
- render_context.conformance_tests_running_context.current_testing_frid,
120
- render_context.plain_source_tree,
121
- render_context.frid_context.linked_resources,
122
- existing_files_content,
123
- memory_files_content,
124
- render_context.module_name,
125
- render_context.conformance_tests_running_context.current_testing_module_name,
126
- render_context.get_required_modules_functionalities(),
127
- previous_frid_code_diff,
128
- existing_conformance_test_files_content,
129
- render_context.conformance_tests_running_context.get_current_acceptance_tests(),
130
- previous_conformance_tests_issue,
131
- render_context.conformance_tests_running_context.fix_attempts,
132
- render_context.conformance_tests_running_context.get_current_conformance_test_folder_name(),
133
- render_context.conformance_tests_running_context.current_testing_frid_high_level_implementation_plan,
134
- render_context.conformance_tests_running_context.conflicting_requirement_count,
135
- run_state=render_context.run_state,
116
+ [issue_reason_code, response_files, fix_attempt_summary, prepared_conformance_tests_issue] = (
117
+ render_context.codeplain_api.fix_conformance_tests_issue(
118
+ render_context.frid_context.frid,
119
+ render_context.conformance_tests_running_context.current_testing_frid,
120
+ render_context.plain_source_tree,
121
+ render_context.frid_context.linked_resources,
122
+ existing_files_content,
123
+ memory_files_content,
124
+ render_context.module_name,
125
+ render_context.conformance_tests_running_context.current_testing_module_name,
126
+ render_context.get_required_modules_functionalities(),
127
+ previous_frid_code_diff,
128
+ existing_conformance_test_files_content,
129
+ render_context.conformance_tests_running_context.get_current_acceptance_tests(),
130
+ previous_conformance_tests_issue,
131
+ render_context.conformance_tests_running_context.fix_attempts,
132
+ render_context.conformance_tests_running_context.get_current_conformance_test_folder_name(),
133
+ render_context.conformance_tests_running_context.current_testing_frid_high_level_implementation_plan,
134
+ render_context.conformance_tests_running_context.conflicting_requirement_count,
135
+ journal.build_digest(ctx.current_testing_module_name, ctx.current_testing_frid),
136
+ run_state=render_context.run_state,
137
+ )
136
138
  )
137
139
  code_diff_files_content = {}
138
140
 
@@ -159,14 +161,21 @@ class FixConformanceTest(BaseAction):
159
161
  existing_conformance_test_files,
160
162
  )
161
163
  code_diff_files_content = diff_utils.get_code_diff(response_files, existing_conformance_test_files_content)
162
- render_context.conformance_tests_running_context.code_diff_files = code_diff_files_content
164
+ journal.record_fix(
165
+ ctx.current_testing_module_name,
166
+ ctx.current_testing_frid,
167
+ fix_attempt_summary,
168
+ list(response_files),
169
+ "CONFORMANCE_TESTS",
170
+ code_diff_files_content,
171
+ prepared_conformance_tests_issue,
172
+ )
163
173
 
164
174
  return self.IMPLEMENTATION_CODE_NOT_UPDATED, None
165
175
  else:
166
176
  if len(response_files) > 0:
167
177
  file_utils.store_response_files(render_context.build_folder, response_files, existing_files)
168
178
  code_diff_files_content = diff_utils.get_code_diff(response_files, existing_files_content)
169
- render_context.conformance_tests_running_context.code_diff_files = code_diff_files_content
170
179
  console.print_files(
171
180
  "Files fixed:",
172
181
  render_context.build_folder,
@@ -180,6 +189,26 @@ class FixConformanceTest(BaseAction):
180
189
  ctx.test_that_triggered_code_change = (ctx.current_testing_module_name, ctx.current_testing_frid)
181
190
  ctx.execution_phase = TestExecutionPhase.RETRYING_AFTER_CODE_CHANGE
182
191
 
192
+ journal.record_fix(
193
+ ctx.current_testing_module_name,
194
+ ctx.current_testing_frid,
195
+ fix_attempt_summary,
196
+ list(response_files),
197
+ "IMPLEMENTATION_CODE",
198
+ code_diff_files_content,
199
+ prepared_conformance_tests_issue,
200
+ )
201
+
183
202
  return self.IMPLEMENTATION_CODE_UPDATED, None
184
203
  else:
204
+ journal.record_fix(
205
+ ctx.current_testing_module_name,
206
+ ctx.current_testing_frid,
207
+ fix_attempt_summary,
208
+ [],
209
+ "IMPLEMENTATION_CODE",
210
+ None,
211
+ prepared_conformance_tests_issue,
212
+ )
213
+
185
214
  return self.IMPLEMENTATION_CODE_NOT_UPDATED, None
@@ -3,6 +3,7 @@ from typing import Any
3
3
  import file_utils
4
4
  import git_utils
5
5
  import plain_spec
6
+ from memory_management import MemoryManager
6
7
  from plain2code_console import console
7
8
  from render_machine.actions.base_action import BaseAction
8
9
  from render_machine.render_context import RenderContext
@@ -50,6 +51,13 @@ class PrepareRepositories(BaseAction):
50
51
  render_context.module_name,
51
52
  render_context.run_state.render_id,
52
53
  )
54
+
55
+ # Like the code repo, the distilled conformance test memories carry over from the
56
+ # previous module so its learnings are available while rendering this one.
57
+ MemoryManager.inherit_memories(
58
+ previous_module.module_memory_folder,
59
+ render_context.plain_module.module_memory_folder,
60
+ )
53
61
  else:
54
62
  console.debug("Initializing git repositories for the render folders.")
55
63
 
@@ -54,18 +54,12 @@ class RunConformanceTests(BaseAction):
54
54
  )
55
55
  render_context.script_execution_history.should_update_script_outputs = True
56
56
 
57
- render_context.memory_manager.create_conformance_tests_memory(
58
- render_context, exit_code, conformance_tests_issue
59
- )
60
-
61
57
  if exit_code == 0:
62
- if (
63
- render_context.conformance_tests_running_context.current_testing_module_name
64
- == render_context.module_name
65
- and render_context.conformance_tests_running_context.current_testing_frid
66
- == render_context.frid_context.frid
67
- ):
68
- render_context.memory_manager.delete_unresolved_memory_files()
58
+ render_context.memory_manager.journal.record_result(
59
+ render_context.conformance_tests_running_context.current_testing_module_name,
60
+ render_context.conformance_tests_running_context.current_testing_frid,
61
+ passed=True,
62
+ )
69
63
  return self.SUCCESSFUL_OUTCOME, None
70
64
 
71
65
  if exit_code in UNRECOVERABLE_ERROR_EXIT_CODES:
@@ -90,10 +90,6 @@ class ConformanceTestsRunningContext:
90
90
  self.regenerating_conformance_tests: bool = False
91
91
 
92
92
  self.current_testing_frid_high_level_implementation_plan: Optional[str] = None
93
- self.previous_conformance_tests_issue_old: Optional[str] = None
94
- self.previous_conformance_tests_issue_frid: Optional[str] = None
95
- self.previous_conformance_tests_issue_module: Optional[str] = None
96
- self.code_diff_files: Optional[dict[str, str]] = None
97
93
 
98
94
  def get_conformance_tests_json(self, module_name: str) -> dict:
99
95
  return self._conformance_tests_json[module_name]
@@ -13,6 +13,7 @@ from render_machine.actions.analyze_specification_ambiguity import AnalyzeSpecif
13
13
  from render_machine.actions.commit_conformance_tests_changes import CommitConformanceTestsChanges
14
14
  from render_machine.actions.commit_implementation_code_changes import CommitImplementationCodeChanges
15
15
  from render_machine.actions.create_dist import CreateDist
16
+ from render_machine.actions.distill_conformance_test_memory import DistillConformanceTestMemory
16
17
  from render_machine.actions.exit_with_error import ExitWithError
17
18
  from render_machine.actions.finish_functional_requirement import FinishFunctionalRequirement
18
19
  from render_machine.actions.fix_conformance_test import FixConformanceTest
@@ -52,6 +53,7 @@ class StateMachineConfig:
52
53
  f"{States.IMPLEMENTING_FRID.value}_{States.PROCESSING_CONFORMANCE_TESTS.value}_{States.CONFORMANCE_TEST_GENERATED.value}": PrepareTestingEnvironment(),
53
54
  f"{States.IMPLEMENTING_FRID.value}_{States.PROCESSING_CONFORMANCE_TESTS.value}_{States.CONFORMANCE_TEST_ENV_PREPARED.value}": RunConformanceTests(),
54
55
  f"{States.IMPLEMENTING_FRID.value}_{States.PROCESSING_CONFORMANCE_TESTS.value}_{States.CONFORMANCE_TEST_FAILED.value}": FixConformanceTest(),
56
+ f"{States.IMPLEMENTING_FRID.value}_{States.PROCESSING_CONFORMANCE_TESTS.value}_{States.POSTPROCESSING_CONFORMANCE_TESTS.value}_{States.CONFORMANCE_TESTS_READY_FOR_MEMORY_DISTILLATION.value}": DistillConformanceTestMemory(),
55
57
  f"{States.IMPLEMENTING_FRID.value}_{States.PROCESSING_CONFORMANCE_TESTS.value}_{States.POSTPROCESSING_CONFORMANCE_TESTS.value}_{States.CONFORMANCE_TESTS_READY_FOR_SUMMARY.value}": SummarizeConformanceTests(),
56
58
  f"{States.IMPLEMENTING_FRID.value}_{States.PROCESSING_CONFORMANCE_TESTS.value}_{States.POSTPROCESSING_CONFORMANCE_TESTS.value}_{States.CONFORMANCE_TESTS_READY_FOR_COMMIT.value}": CommitConformanceTestsChanges(
57
59
  git_utils.CONFORMANCE_TESTS_PASSED_COMMIT_MESSAGE,
@@ -96,6 +98,7 @@ class StateMachineConfig:
96
98
  FixConformanceTest.REGENERATE_CONFORMANCE_TESTS_OUTCOME: triggers.MARK_REGENERATION_OF_CONFORMANCE_TESTS,
97
99
  CommitConformanceTestsChanges.SUCCESSFUL_OUTCOME_IMPLEMENTATION_UPDATED: triggers.MARK_NEXT_CONFORMANCE_TESTS_POSTPROCESSING_STEP,
98
100
  CommitConformanceTestsChanges.SUCCESSFUL_OUTCOME_IMPLEMENTATION_NOT_UPDATED: triggers.PROCEED_FRID_PROCESSING,
101
+ DistillConformanceTestMemory.SUCCESSFUL_OUTCOME: triggers.MARK_NEXT_CONFORMANCE_TESTS_POSTPROCESSING_STEP,
99
102
  SummarizeConformanceTests.SUCCESSFUL_OUTCOME: triggers.MARK_NEXT_CONFORMANCE_TESTS_POSTPROCESSING_STEP,
100
103
  AnalyzeSpecificationAmbiguity.SUCCESSFUL_OUTCOME: triggers.PROCEED_FRID_PROCESSING,
101
104
  }
@@ -120,8 +123,9 @@ class StateMachineConfig:
120
123
  def get_postprocessing_conformance_tests_states(self) -> Dict[str, Any]:
121
124
  return {
122
125
  "name": States.POSTPROCESSING_CONFORMANCE_TESTS.value,
123
- "initial": States.CONFORMANCE_TESTS_READY_FOR_SUMMARY.value,
126
+ "initial": States.CONFORMANCE_TESTS_READY_FOR_MEMORY_DISTILLATION.value,
124
127
  "children": [
128
+ States.CONFORMANCE_TESTS_READY_FOR_MEMORY_DISTILLATION.value,
125
129
  States.CONFORMANCE_TESTS_READY_FOR_SUMMARY.value,
126
130
  States.CONFORMANCE_TESTS_READY_FOR_COMMIT.value,
127
131
  States.CONFORMANCE_TESTS_READY_FOR_AMBIGUITY_ANALYSIS.value,
@@ -278,6 +282,11 @@ class StateMachineConfig:
278
282
  "trigger": triggers.MARK_ALL_CONFORMANCE_TESTS_PASSED,
279
283
  "dest": f"{States.IMPLEMENTING_FRID.value}_{States.PROCESSING_CONFORMANCE_TESTS.value}_{States.POSTPROCESSING_CONFORMANCE_TESTS.value}",
280
284
  },
285
+ {
286
+ "source": f"{States.IMPLEMENTING_FRID.value}_{States.PROCESSING_CONFORMANCE_TESTS.value}_{States.POSTPROCESSING_CONFORMANCE_TESTS.value}_{States.CONFORMANCE_TESTS_READY_FOR_MEMORY_DISTILLATION.value}",
287
+ "trigger": triggers.MARK_NEXT_CONFORMANCE_TESTS_POSTPROCESSING_STEP,
288
+ "dest": f"{States.IMPLEMENTING_FRID.value}_{States.PROCESSING_CONFORMANCE_TESTS.value}_{States.POSTPROCESSING_CONFORMANCE_TESTS.value}_{States.CONFORMANCE_TESTS_READY_FOR_SUMMARY.value}",
289
+ },
281
290
  {
282
291
  "source": f"{States.IMPLEMENTING_FRID.value}_{States.PROCESSING_CONFORMANCE_TESTS.value}_{States.POSTPROCESSING_CONFORMANCE_TESTS.value}_{States.CONFORMANCE_TESTS_READY_FOR_SUMMARY.value}",
283
292
  "trigger": triggers.MARK_NEXT_CONFORMANCE_TESTS_POSTPROCESSING_STEP,
@@ -44,6 +44,7 @@ class States(Enum):
44
44
 
45
45
  # Postprocessing conformance tests states
46
46
  POSTPROCESSING_CONFORMANCE_TESTS = "postprocessingConformanceTests"
47
+ CONFORMANCE_TESTS_READY_FOR_MEMORY_DISTILLATION = "conformanceTestsReadyForMemoryDistillation"
47
48
  CONFORMANCE_TESTS_READY_FOR_SUMMARY = "conformanceTestsReadyForSummary"
48
49
  CONFORMANCE_TESTS_READY_FOR_COMMIT = "conformanceTestsReadyForCommit"
49
50
  CONFORMANCE_TESTS_READY_FOR_AMBIGUITY_ANALYSIS = "conformanceTestsReadyForAmbiguityAnalysis"