codeplain 0.2.20__tar.gz → 0.3.0__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.
- codeplain-0.3.0/.claude/docs/PLAIN_REFERENCE.md +325 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/.claude/settings.local.json +3 -1
- codeplain-0.3.0/.github/dependabot.yml +11 -0
- codeplain-0.3.0/.zed/settings.json +61 -0
- codeplain-0.3.0/CLAUDE.md +113 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/PKG-INFO +5 -5
- {codeplain-0.2.20 → codeplain-0.3.0}/README.md +1 -1
- codeplain-0.3.0/_version.py +1 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/codeplain_REST_api.py +30 -10
- {codeplain-0.2.20 → codeplain-0.3.0}/concept_utils.py +1 -1
- {codeplain-0.2.20 → codeplain-0.3.0}/file_utils.py +10 -9
- {codeplain-0.2.20 → codeplain-0.3.0}/git_utils.py +70 -2
- codeplain-0.3.0/module_renderer.py +173 -0
- codeplain-0.3.0/partial_rendering.py +224 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/plain2code.py +98 -139
- {codeplain-0.2.20 → codeplain-0.3.0}/plain2code_arguments.py +6 -4
- {codeplain-0.2.20 → codeplain-0.3.0}/plain2code_events.py +6 -1
- {codeplain-0.2.20 → codeplain-0.3.0}/plain2code_exceptions.py +4 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/plain2code_logger.py +8 -4
- {codeplain-0.2.20 → codeplain-0.3.0}/plain2code_nodes.py +14 -3
- {codeplain-0.2.20 → codeplain-0.3.0}/plain_file.py +39 -25
- codeplain-0.3.0/plain_modules.py +344 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/plain_spec.py +53 -1
- {codeplain-0.2.20 → codeplain-0.3.0}/pyproject.toml +3 -3
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/actions/analyze_specification_ambiguity.py +1 -1
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/actions/fix_conformance_test.py +56 -27
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/actions/fix_unit_tests.py +13 -14
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/actions/prepare_repositories.py +14 -3
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/actions/refactor_code.py +8 -10
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/actions/render_conformance_tests.py +76 -59
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/actions/summarize_conformance_tests.py +9 -12
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/code_renderer.py +20 -7
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/conformance_tests.py +1 -1
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/render_context.py +249 -69
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/render_types.py +57 -9
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/render_utils.py +6 -3
- {codeplain-0.2.20 → codeplain-0.3.0}/requirements.txt +3 -3
- codeplain-0.3.0/test_scripts/run_conformance_tests_cypress.ps1 +324 -0
- codeplain-0.3.0/test_scripts/run_conformance_tests_golang.ps1 +100 -0
- codeplain-0.3.0/test_scripts/run_conformance_tests_python.ps1 +92 -0
- codeplain-0.3.0/test_scripts/run_unittests_flutter.ps1 +82 -0
- codeplain-0.3.0/test_scripts/run_unittests_golang.ps1 +68 -0
- codeplain-0.3.0/test_scripts/run_unittests_python.ps1 +76 -0
- codeplain-0.3.0/test_scripts/run_unittests_react.ps1 +83 -0
- codeplain-0.3.0/tests/data/partial_rendering/pr_leaf.plain +9 -0
- codeplain-0.3.0/tests/data/partial_rendering/pr_middle.plain +14 -0
- codeplain-0.3.0/tests/data/partial_rendering/pr_root.plain +14 -0
- codeplain-0.3.0/tests/data/partial_rendering/pr_solo.plain +11 -0
- codeplain-0.3.0/tests/test_file_utils.py +41 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/test_git_utils.py +74 -0
- codeplain-0.3.0/tests/test_partial_rendering.py +316 -0
- codeplain-0.3.0/tests/test_plain_modules.py +294 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/test_plainfile.py +1 -1
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/test_plainfileparser.py +2 -2
- {codeplain-0.2.20 → codeplain-0.3.0}/tui/components.py +73 -11
- {codeplain-0.2.20 → codeplain-0.3.0}/tui/plain2code_tui.py +21 -30
- codeplain-0.3.0/tui/plain_module_render_choice_tui.py +186 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tui/state_handlers.py +9 -4
- {codeplain-0.2.20 → codeplain-0.3.0}/tui/styles.css +42 -3
- {codeplain-0.2.20 → codeplain-0.3.0}/tui/widget_helpers.py +24 -1
- codeplain-0.2.20/_version.py +0 -1
- codeplain-0.2.20/module_renderer.py +0 -279
- codeplain-0.2.20/plain_modules.py +0 -132
- {codeplain-0.2.20 → codeplain-0.3.0}/.flake8 +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/.github/workflows/lint-and-test.yml +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/.github/workflows/nofity-slack-on-main-merge.yml +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/.github/workflows/publish-install-script.yml +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/.github/workflows/publish-to-pypi.yml +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/.gitignore +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/.vscode/launch.json +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/.vscode/settings.json +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/LICENSE +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/config/__init__.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/config/system_config.yaml +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/diff_utils.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/docs/generate_cli.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/docs/plain2code_cli.md +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/docs/starting_a_plain_project_from_scratch.md +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/event_bus.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/examples/example_hello_world_golang/config.yaml +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/examples/example_hello_world_golang/harness_tests/hello_world_test.go +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/examples/example_hello_world_golang/hello_world_golang.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/examples/example_hello_world_golang/run.sh +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/examples/example_hello_world_python/config.yaml +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/examples/example_hello_world_python/harness_tests/hello_world_display/test_hello_world.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/examples/example_hello_world_python/hello_world_python.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/examples/example_hello_world_python/run.sh +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/examples/example_hello_world_react/config.yaml +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress/e2e/hello_world.cy.ts +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress/support/e2e.ts +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/examples/example_hello_world_react/harness_tests/hello_world_display/cypress.config.ts +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/examples/example_hello_world_react/harness_tests/hello_world_display/package.json +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/examples/example_hello_world_react/harness_tests/hello_world_display/tsconfig.json +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/examples/example_hello_world_react/hello_world_react.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/examples/example_hello_world_react/run.sh +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/examples/run.sh +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/hash_key.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/install/bash/examples.sh +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/install/bash/install.sh +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/install/bash/walkthrough.sh +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/install/powershell/examples.ps1 +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/install/powershell/install.ps1 +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/install/powershell/walkthrough.ps1 +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/memory_management.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/plain2code_console.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/plain2code_read_config.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/plain2code_state.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/plain2code_utils.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/pytest.ini +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/__init__.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/actions/base_action.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/actions/commit_conformance_tests_changes.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/actions/commit_implementation_code_changes.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/actions/create_dist.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/actions/exit_with_error.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/actions/finish_functional_requirement.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/actions/prepare_testing_environment.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/actions/render_functional_requirement.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/actions/run_conformance_tests.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/actions/run_unit_tests.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/conformance_test_helpers.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/implementation_code_helpers.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/state_machine_config.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/states.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/render_machine/triggers.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/resources/codeplain_overview.png +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/resources/plain_example.png +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/standard_template_library/__init__.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/standard_template_library/golang-console-app-template.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/standard_template_library/python-console-app-template.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/standard_template_library/typescript-react-app-boilerplate.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/standard_template_library/typescript-react-app-template.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/system_config.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/test_scripts/run_conformance_tests_cypress.sh +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/test_scripts/run_conformance_tests_golang.sh +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/test_scripts/run_conformance_tests_python.sh +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/test_scripts/run_unittests_golang.sh +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/test_scripts/run_unittests_python.sh +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/test_scripts/run_unittests_react.sh +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/__init__.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/conftest.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/imports/circular_imports_1.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/imports/circular_imports_2.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/imports/circular_imports_main.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/imports/diamond_import_1.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/imports/diamond_import_2.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/imports/diamond_import_common.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/imports/diamond_imports_main.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/imports/non_existent_import.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfile/duplicate_specification_heading.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfile/invalid_specification_order.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfile/missing_non_functional_requirements.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfile/plain_source_with_absolute_link.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfile/plain_source_with_url_link.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfile/task_manager_with_reference_links.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfile/without_non_functional_requirement.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/concept_validation_acceptance_tests.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/concept_validation_acceptance_tests_nondefined.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/concept_validation_defined_nondefined.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/concept_validation_defined_nondefined_2.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/concept_validation_definition.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/concept_validation_noconcepts.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/concept_validation_nonconcept.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/concept_validation_nondefined.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/concept_validation_redefinition.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/concept_validation_several_concepts.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/concept_validation_valid.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/exported_concepts.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/exported_concepts_base.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/exported_concepts_example.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/exported_concepts_missing.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/exported_concepts_missing_example.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/exported_concepts_nested.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/exported_concepts_nested_example.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/exported_concepts_transitive_example.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/exported_concepts_transitive_l1.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/exported_concepts_transitive_l2.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/plain_file_parser_with_comments.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/plain_file_with_comments_indented.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/regular_plain_source.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/required_concepts.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/required_concepts_defs.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/required_concepts_example.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/required_concepts_l1.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/required_concepts_l2.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/required_concepts_missing.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/required_concepts_module.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/required_concepts_partial.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/required_concepts_partial_duplicate.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/topological_sort.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/plainfileparser/topological_sort_not_referenced.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/requires/circular_requires_main.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/requires/circular_requires_sub.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/requires/diamond_requires_1.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/requires/diamond_requires_2.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/requires/diamond_requires_common.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/requires/diamond_requires_main.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/requires/independent_requires_1.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/requires/independent_requires_2.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/requires/independent_requires_main.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/requires/non_existent_require.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/requires/normal_requires_1.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/requires/normal_requires_2.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/requires/normal_requires_common.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/requires/normal_requires_main.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/simple.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/templates/block_level_include.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/templates/code_variables.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/templates/header.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/templates/implement.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/templates/implement_2.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/templates/template_include.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/data/templates/test_hardest_problem.plain +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/test_imports.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/test_plainspec.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/test_requires.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tests/test_resolve_config_file.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tui/__init__.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tui/models.py +0 -0
- {codeplain-0.2.20 → codeplain-0.3.0}/tui/spinner.py +0 -0
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
## Project Overview
|
|
2
|
+
|
|
3
|
+
This repository is a workspace for writing and managing **\*\*\*plain** (codeplain) specifications. \*\*\*plain is a specification-driven language powered by AI that generates production-ready code from `.plain` spec files.
|
|
4
|
+
|
|
5
|
+
The `.plain` files in this repository are the source of truth. They describe what the software should do, how it should be built, and how it should be tested. The generated code is a read-only artifact produced by the renderer.
|
|
6
|
+
|
|
7
|
+
## ***plain Language Reference
|
|
8
|
+
|
|
9
|
+
***plain is a specification language designed for writing software requirements in a clear, structured format. It generates production-ready code from `.plain` spec files using AI. Full documentation: https://plainlang.org/docs/language-guide/
|
|
10
|
+
|
|
11
|
+
### .plain File Structure
|
|
12
|
+
|
|
13
|
+
A `.plain` file consists of an optional YAML frontmatter section followed by standardized sections marked with `***section name***` headers. There are four types of specification sections:
|
|
14
|
+
|
|
15
|
+
- `***definitions***` — declares concepts used throughout the specification
|
|
16
|
+
- `***implementation reqs***` — non-functional requirements about how the software should be built
|
|
17
|
+
- `***test reqs***` — requirements for conformance testing
|
|
18
|
+
- `***functional specs***` — describes what the software should do
|
|
19
|
+
|
|
20
|
+
Every plain source file requires at least one functional spec and an associated implementation req. Functional specs must reside in leaf sections while other specifications can be placed also in non-leaf sections. Specifications in non-leaf sections apply to all of their subsections.
|
|
21
|
+
|
|
22
|
+
### Concept Notation
|
|
23
|
+
|
|
24
|
+
Concepts are the building blocks of ***plain specifications. They are written between colons: `:ConceptName:`. Valid characters include letters, digits, plus, minus, dot, and underscore.
|
|
25
|
+
|
|
26
|
+
Concepts must be defined in `***definitions***` before being referenced in other sections. Concept names must be globally unique across the specification and its imports. Concept references must not form cycles — if concept A references concept B, then concept B must not reference concept A (directly or indirectly).
|
|
27
|
+
|
|
28
|
+
Example:
|
|
29
|
+
|
|
30
|
+
```plain
|
|
31
|
+
***definitions***
|
|
32
|
+
- :User: is the user of :App:
|
|
33
|
+
- :Task: describes an activity that needs to be done by :User:. :Task: has:
|
|
34
|
+
- Name - a short description (required)
|
|
35
|
+
- Notes - additional details (optional)
|
|
36
|
+
- Due Date - completion deadline (optional)
|
|
37
|
+
- :TaskList: is a list of :Task: items.
|
|
38
|
+
- Initially :TaskList: should be empty.
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Predefined Concepts
|
|
42
|
+
|
|
43
|
+
***plain provides predefined concepts available in all specifications without needing to be defined:
|
|
44
|
+
|
|
45
|
+
| Concept | Meaning |
|
|
46
|
+
|---------|---------|
|
|
47
|
+
| `:plainDefinitions:` | Content of the `***definitions***` section |
|
|
48
|
+
| `:plainImplementationReqs:` | Content of the `***implementation reqs***` section |
|
|
49
|
+
| `:plainFunctionality:` | Content of the `***functional specs***` section |
|
|
50
|
+
| `:plainTestReqs:` | Content of the `***test reqs***` section |
|
|
51
|
+
| `:Implementation:` | The system implementing `:plainFunctionality:` |
|
|
52
|
+
| `:plainImplementationCode:` | The generated implementation code |
|
|
53
|
+
| `:UnitTests:` | Auto-generated unit tests for individual functionalities |
|
|
54
|
+
| `:ConformanceTests:` | Auto-generated tests that verify implementation conforms to specs |
|
|
55
|
+
| `:AcceptanceTest:` / `:AcceptanceTests:` | Tests that validate specific aspects of the implementation |
|
|
56
|
+
|
|
57
|
+
### Definitions Section
|
|
58
|
+
|
|
59
|
+
Declares concepts used throughout the specification. A concept must be defined before it can be referenced in any section. The definition can come from the module's own `***definitions***` section, from an `import`ed module's definitions, or from a `require`d module's `exported_concepts`. Attributes and constraints can be nested as sub-bullets.
|
|
60
|
+
|
|
61
|
+
```plain
|
|
62
|
+
***definitions***
|
|
63
|
+
- :ConceptName: is a description of the concept.
|
|
64
|
+
- Additional details or attributes can be nested
|
|
65
|
+
- Multiple attributes can be listed
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Implementation Reqs Section
|
|
69
|
+
|
|
70
|
+
A free-form section for any instructions that steer code generation. Common uses include technology choices, architectural constraints, coding standards, and naming conventions, but it can also contain detailed implementation guidance — data formats, error handling strategies, algorithm descriptions, or any other context the renderer needs to produce correct code. These describe HOW to build the software, not WHAT it should do.
|
|
71
|
+
|
|
72
|
+
```plain
|
|
73
|
+
***implementation reqs***
|
|
74
|
+
- :Implementation: should be in Python.
|
|
75
|
+
- :MainExecutableFile: of :App: should be called "hello_world.py".
|
|
76
|
+
- :Implementation: should include :Unittests: using Unittest framework!
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Test Reqs Section
|
|
80
|
+
|
|
81
|
+
Specifies requirements for conformance testing — test frameworks, execution methods, and testing constraints. Only used when writing and fixing conformance tests (not unit tests).
|
|
82
|
+
|
|
83
|
+
```plain
|
|
84
|
+
***test reqs***
|
|
85
|
+
- :ConformanceTests: of :App: should be implemented in Python using Unittest framework.
|
|
86
|
+
- :ConformanceTests: will be run using "python -m unittest discover" command.
|
|
87
|
+
- :ConformanceTests: must be implemented and executed - do not use unittest.skip().
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Functional Specs Section
|
|
91
|
+
|
|
92
|
+
Describes what the software should do. Each bullet point is a single piece of functionality that will be implemented. Functional specs are rendered incrementally one by one — earlier specs cannot reference later specs.
|
|
93
|
+
|
|
94
|
+
Each functional spec must be limited in complexity. If a spec is too complex, the renderer responds with "Functional spec too complex!" and it must be broken down into smaller specs.
|
|
95
|
+
|
|
96
|
+
Functional specs are in **chronological order** — earlier specs are rendered before later ones. Functional specs defined in `requires` modules are considered **previous functional requirements** relative to the current module's specs. This ordering matters for incremental rendering and for detecting conflicts between requirements.
|
|
97
|
+
|
|
98
|
+
The renderer has **no knowledge of future functional requirements**. When a functional spec is being implemented, only the previous functional specs (those already rendered) are in the renderer's context. Specs that come later in the list are invisible to the renderer at that point. This means each spec is implemented without any awareness of what will come next.
|
|
99
|
+
|
|
100
|
+
```plain
|
|
101
|
+
***functional specs***
|
|
102
|
+
- Implement the entry point for :App:.
|
|
103
|
+
- Show :TaskList:.
|
|
104
|
+
- :User: should be able to add :Task:. Only valid :Task: items can be added.
|
|
105
|
+
- :User: should be able to delete :Task:.
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Each functional spec must be unambiguous. If a single line is not enough to fully disambiguate the behavior, use nested sub-bullets to add detail. Nested lines clarify the parent spec — they do not introduce separate functionality. Even with nested detail, the spec must still respect the complexity limit.
|
|
109
|
+
|
|
110
|
+
```plain
|
|
111
|
+
***functional specs***
|
|
112
|
+
- :User: should be able to send a :Message: to a :Conversation:.
|
|
113
|
+
- A :Message: must have non-empty content.
|
|
114
|
+
- The :Message: is appended to the end of the :Conversation:.
|
|
115
|
+
- All :Participant: members of the :Conversation: can see the new :Message:.
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Conformance-Testing Tags
|
|
119
|
+
|
|
120
|
+
A functional spec can be tagged at the start of its bullet to control whether it goes through the conformance-testing pipeline:
|
|
121
|
+
|
|
122
|
+
- `[cf]` — **opt-in** to full conformance testing for this spec (spec-derived tests + acceptance tests).
|
|
123
|
+
- `[ncf]` — **opt-out** of conformance testing entirely for this spec.
|
|
124
|
+
|
|
125
|
+
Tags are stripped from the rendered text — they never reach the model. Both tags are case-insensitive (`[CF]`, `[NCF]` work) but must be the leading token of the bullet.
|
|
126
|
+
|
|
127
|
+
```plain
|
|
128
|
+
***functional specs***
|
|
129
|
+
|
|
130
|
+
- [cf] Display "hello, world"
|
|
131
|
+
- Show :TaskList:.
|
|
132
|
+
- [ncf] Internal-only debug counter.
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Per-FRID resolution rules:
|
|
136
|
+
|
|
137
|
+
| Spec tag | Module has any tag? | Spec has `***acceptance tests***` | Resolved mode |
|
|
138
|
+
|---|---|---|---|
|
|
139
|
+
| `[cf]` | — | — | Full conformance |
|
|
140
|
+
| `[ncf]` | — | — | Skip |
|
|
141
|
+
| _untagged_ | no | — | Full conformance (backward compat) |
|
|
142
|
+
| _untagged_ | yes | yes | Acceptance-only (skip spec-derived tests, still run acceptance tests) |
|
|
143
|
+
| _untagged_ | yes | no | Skip |
|
|
144
|
+
|
|
145
|
+
Common patterns:
|
|
146
|
+
|
|
147
|
+
- **Skip a whole module**: tag every functional spec with `[ncf]`. The module's specs all resolve to skip.
|
|
148
|
+
- **Opt-in only specific specs**: tag the relevant specs with `[cf]`; leave others untagged. Untagged specs follow the acceptance-only / skip rules above.
|
|
149
|
+
- **Mix opt-in and explicit opt-out**: combine `[cf]` and `[ncf]` freely. Either tag puts the module into "any tag" mode for the rules above.
|
|
150
|
+
|
|
151
|
+
Constraints:
|
|
152
|
+
|
|
153
|
+
- A spec carrying `[cf]` requires `--conformance-tests-script` (or `conformance-tests-script:` in `config.yaml`); otherwise the renderer errors out before any code is generated. `[ncf]` does not require the script.
|
|
154
|
+
- If a spec was previously rendered in full conformance and is later switched to `[ncf]` or otherwise resolved as skip / acceptance-only, existing files under `conformance_tests/<module>/<frid>/` are kept and a warning is logged. They are not regenerated and not run. Delete the folder manually for a clean state.
|
|
155
|
+
|
|
156
|
+
### Acceptance Tests
|
|
157
|
+
|
|
158
|
+
Nested under individual functional requirements to specify how to verify correct implementation. They extend conformance tests and are implemented according to the `***test reqs***` specification.
|
|
159
|
+
|
|
160
|
+
```plain
|
|
161
|
+
***functional specs***
|
|
162
|
+
- Display "hello, world"
|
|
163
|
+
|
|
164
|
+
***acceptance tests***
|
|
165
|
+
- :App: should exit with status code 0 indicating successful execution.
|
|
166
|
+
- :App: should complete execution in under 1 second.
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### YAML Frontmatter
|
|
170
|
+
|
|
171
|
+
The frontmatter is enclosed between `---` markers and supports:
|
|
172
|
+
|
|
173
|
+
- **`import`** — includes definitions, implementation reqs, and test reqs from templates. Imported modules must not contain functional specs. The default import directory is `template/` — the `template/` prefix is not needed (e.g., `airplain` resolves to `template/airplain.plain`).
|
|
174
|
+
- **`requires`** — specifies dependencies on other root-level modules that must be built first. Unlike `import`, required modules can contain functional specs and represent complete software modules. Requires paths point to root-level modules (e.g., `auth`, `messaging`).
|
|
175
|
+
- **`description`** — optional description of the specification.
|
|
176
|
+
- **`required_concepts`** — concepts that must be defined by any module that imports this spec.
|
|
177
|
+
- **`exported_concepts`** — concepts made available to modules that `require` this one.
|
|
178
|
+
|
|
179
|
+
### Linked Resources
|
|
180
|
+
|
|
181
|
+
Specifications can reference external files using markdown link syntax. The linked resource is passed along with the spec to the renderer. File paths are resolved relative to the `.plain` file location. Only files in the same folder (and subfolders) are supported; no external URLs.
|
|
182
|
+
|
|
183
|
+
```plain
|
|
184
|
+
- :User: should be able to add :Task:. The details of the user interface
|
|
185
|
+
are provided in the file [task_modal_specification.yaml](task_modal_specification.yaml).
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Template System
|
|
189
|
+
|
|
190
|
+
***plain supports template inclusion using `{% include %}` syntax:
|
|
191
|
+
|
|
192
|
+
```plain
|
|
193
|
+
{% include "python-console-app-template.plain", main_executable_file_name: "my_app.py" %}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Parameters are passed as key-value pairs. Inside the template, they are accessed using variable syntax (`{{ variable_name }}`). Only variables are supported — conditionals, loops, and other Liquid features are not available.
|
|
197
|
+
|
|
198
|
+
### Comments
|
|
199
|
+
|
|
200
|
+
Lines starting with `>` are ignored when rendering:
|
|
201
|
+
|
|
202
|
+
```plain
|
|
203
|
+
> This is a comment in ***plain
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Best Practices
|
|
207
|
+
|
|
208
|
+
1. **Reference concepts consistently** — use `:ConceptName:` notation to disambiguate key concepts
|
|
209
|
+
2. **Keep it simple** — specs should be readable by both humans and AI
|
|
210
|
+
3. **Leverage templates** — use the standard template library for common patterns
|
|
211
|
+
4. **Use acceptance tests** — add them for requirements that need verification
|
|
212
|
+
5. **Be specific** — write clear, testable requirements in functional specs
|
|
213
|
+
6. **Define before use** — always define concepts in `***definitions***` before referencing them
|
|
214
|
+
7. **Start with imports** — import relevant templates before defining your own concepts
|
|
215
|
+
|
|
216
|
+
## Repository Structure
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
*.plain # Specification files (the source of truth)
|
|
220
|
+
template/*.plain # Reusable template specs imported by module specs
|
|
221
|
+
plain_modules/ # Generated code output (one folder per .plain spec)
|
|
222
|
+
resources/ # Schemas, API specs, transforms, test fixtures
|
|
223
|
+
conformance_tests/ # Generated conformance tests (one folder per module)
|
|
224
|
+
test_scripts/ # Scripts for running unit and conformance tests
|
|
225
|
+
config.yaml # Codeplain configuration
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
**Generated artifacts** (gitignored):
|
|
229
|
+
- `plain_modules/<module_name>/` — generated project for each `.plain` spec (implementation + unit tests)
|
|
230
|
+
- `conformance_tests/<module_name>/` — generated conformance tests for each module
|
|
231
|
+
|
|
232
|
+
## How Modules Work
|
|
233
|
+
|
|
234
|
+
There are two types of modules:
|
|
235
|
+
|
|
236
|
+
### Import Modules
|
|
237
|
+
|
|
238
|
+
An import module lives in the **`template/`** directory and contains **only** `***definitions***`, `***implementation reqs***`, and/or `***test reqs***`. It must **not** contain `***functional specs***` and must **not** use `requires`. It may optionally `import` other templates for layered reuse.
|
|
239
|
+
|
|
240
|
+
When a module **`import`s** another, it gains access to the imported module's definitions, implementation reqs, and test reqs — but not its functional specs. The default import directory is `template/`, so the `template/` prefix is not needed (e.g., `airplain`).
|
|
241
|
+
|
|
242
|
+
### Requires Modules
|
|
243
|
+
|
|
244
|
+
`requires` establishes a build ordering between modules. The required module is built **before** the current one. This does not necessarily mean the current module extends or depends on the required module's code — it may be completely independent. The `requires` relationship ensures the build order is correct.
|
|
245
|
+
|
|
246
|
+
When a module **`requires`** another:
|
|
247
|
+
- The required module's generated code (`plain_modules/<required_module>`) is copied as the starting point.
|
|
248
|
+
- The required module's `***functional specs***` become visible as **previous functional requirements**.
|
|
249
|
+
- Only `exported_concepts` from the required module are available (not its full definitions).
|
|
250
|
+
|
|
251
|
+
A module can use both `requires` and `import` together. `requires` points to other root-level modules (e.g., `auth`, `messaging`); `import` resolves from the default `template/` directory without needing the prefix (e.g., `airplain`). Modules with functional specs live at the repository root. Import modules (templates) live in `template/`.
|
|
252
|
+
|
|
253
|
+
**`requires` modules must share the same tech stack.** Because the required module's generated code is copied as the starting point and the renderer continues building on top of it with one language/framework toolchain, two modules can only be linked with `requires` when they target the same language, framework, and runtime. A runtime/network dependency between systems is **not** a reason to use `requires`. For example, a React frontend that talks to a Python/FastAPI backend over HTTP must **not** `requires: [backend]` — the stacks differ. Model that pair as two independent root modules (each with its own `config.yaml` and test scripts), and express the contract between them through a shared API schema in `resources/` or shared concepts in an `import`ed template, not through `requires`.
|
|
254
|
+
|
|
255
|
+
### Contracts Between Modules
|
|
256
|
+
|
|
257
|
+
Modules can use `required_concepts` and `exported_concepts` to enforce contracts between them. A template declaring `required_concepts` means any module that imports it must define those concepts. A module declaring `exported_concepts` controls which concepts are visible to modules that `require` it.
|
|
258
|
+
|
|
259
|
+
**Exported concepts are not transitive.** If module A exports a concept and module B `requires` A, module B can use that concept — but if module C `requires` B, it does **not** automatically gain access to A's exported concepts. If a concept needs to be shared across multiple `requires` modules, define it in a common import module and have each module `import` that shared template.
|
|
260
|
+
|
|
261
|
+
## Running Tests
|
|
262
|
+
|
|
263
|
+
Test scripts live in `test_scripts/` and are run from the repo root:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
# Run all unit tests for a module
|
|
267
|
+
./test_scripts/run_unittests.sh <module_name>
|
|
268
|
+
|
|
269
|
+
# Run a single unit test
|
|
270
|
+
./test_scripts/run_unittests_single.sh <module_name>
|
|
271
|
+
|
|
272
|
+
# Run conformance tests
|
|
273
|
+
./test_scripts/run_conformance_tests.sh <module_name> <conformance_tests_folder>
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## Writing Functional Requirements
|
|
277
|
+
|
|
278
|
+
- Each functional requirement must imply a **maximum of 200 changed lines of code**. This is a hard limit — if a requirement would result in more than 200 lines of changes, it must be broken down into smaller, independent requirements. This limit also helps avoid "Functional spec too complex!" errors from the renderer.
|
|
279
|
+
- **Conflicting requirements must be avoided at all costs.** Functional specs should be written so that no conflicts exist between them. If two specs appear to conflict, they must be clarified by adding more detail and context to the specs until all possible conflicts are resolved. Prevention is always preferable to debugging conflicts after rendering.
|
|
280
|
+
- **Specs should be language-agnostic.** Avoid using programming language-specific terminology (e.g., generics syntax, framework annotations, language-specific collection types) in functional specs and definitions. Write requirements in terms of behavior, concepts, and domain logic — not implementation constructs. General technical terms that are not language-specific are fine (e.g., null values, JSON types, HTTP status codes, REST api endpoints etc.). The `***implementation reqs***` section is the appropriate place for language-specific guidance.
|
|
281
|
+
- **Keep sentences short and clear.** Spec lines should be easy to read and understand at a glance. Prefer several short, concise sentences over long, complex ones.
|
|
282
|
+
- **Specs must be deterministic enough to use the software without reading the generated code.** A developer should be able to know exactly how to interact with the built software solely from the specs. For example, if the software is a REST API, the specs must include endpoint paths, HTTP methods, request/response formats, and status codes. If it is a CLI tool, the specs must include command names, arguments, and expected output. Never leave interface details up to the renderer's discretion.
|
|
283
|
+
- **Encapsulate functionality in functional specs.** `requires` modules import only functional specs. It is therefore important that the functionality is encapsulated in the functional specs and not in implementation reqs, as those will not be in the context of future functional specs when fixing previous conformance tests of previous functional requirements.
|
|
284
|
+
|
|
285
|
+
## Working with Specs
|
|
286
|
+
|
|
287
|
+
- The `.plain` files are the source of truth. Modify specs to change behavior, then re-render.
|
|
288
|
+
- The `template/` directory contains reusable template specs that define common patterns.
|
|
289
|
+
- The `resources/` directory contains schemas, API specs, transforms, and test fixtures referenced by the specs.
|
|
290
|
+
- Generated code in `plain_modules/` should not be manually edited — changes will be overwritten on the next render.
|
|
291
|
+
|
|
292
|
+
## Read-Only Generated Artifacts
|
|
293
|
+
|
|
294
|
+
All code in `plain_modules/` and `conformance_tests/` is generated and **must never be modified directly** — not the implementation code, not the unit tests, not the conformance tests. These artifacts can only be:
|
|
295
|
+
|
|
296
|
+
- **Read** — to understand what the generated code does, inspect behavior, and identify ambiguities in the specs.
|
|
297
|
+
- **Tested** — unit tests and conformance tests can be executed to verify correctness.
|
|
298
|
+
- **Debugged** — test failures and unexpected behavior should be traced through the generated code to understand root causes, but fixes must always be applied in the `.plain` specs, never in the generated code.
|
|
299
|
+
|
|
300
|
+
Each module has its own folder under `plain_modules/<module_name>/` containing the generated project (implementation + unit tests). Each module also has its own folder under `conformance_tests/<module_name>/`, with individual subfolders per functionality for conformance tests. Conformance tests may also include generated `***acceptance tests***` — these are equally read-only and serve the same purpose: gathering information and debugging the specs.
|
|
301
|
+
|
|
302
|
+
To change the generated code, **only the corresponding `.plain` spec files may be edited**:
|
|
303
|
+
- To change implementation or unit tests → modify the `***functional specs***`, `***implementation reqs***` or `***definitions***` sections of the spec.
|
|
304
|
+
- To guide conformance test generation → modify the `***test reqs***` section of the spec.
|
|
305
|
+
- To guide acceptance test generation → modify the `***acceptance tests***` subsections under functional specs.
|
|
306
|
+
|
|
307
|
+
The `test_scripts/` folder contains shell scripts for running unit tests and conformance tests against the generated code. These scripts are the entry point for test execution — see the [Running Tests](#running-tests) section for usage.
|
|
308
|
+
|
|
309
|
+
The workflow is: read the generated code to understand what it does, identify what is ambiguous or incorrect in the specs, then make changes exclusively in the `.plain` files and re-render.
|
|
310
|
+
|
|
311
|
+
## Conformance Test Workflow
|
|
312
|
+
|
|
313
|
+
Each functional spec in a module has its own set of conformance tests, generated per functional spec per module. After a new functional spec is rendered (i.e., its implementation code is generated), conformance tests for that spec are also rendered. Before proceeding, **all previous conformance tests** (from earlier functional specs in the same module) are run. Ideally, all conformance tests of all previous functional specs pass without any changes. If any previously passing conformance test now fails, the failure must be resolved before moving on. Resolution means one of three things: fixing the conformance test, fixing the implementation code (by adjusting the spec), or identifying conflicting requirements.
|
|
314
|
+
|
|
315
|
+
If conformance tests of a previous functional spec need to be changed in order to pass, this is a strong indicator that the functional specs themselves may need to be amended. Needing to modify earlier conformance tests suggests the new functional spec has introduced behavior that is inconsistent with what was previously specified — the specs should be reviewed and clarified to eliminate the ambiguity or conflict.
|
|
316
|
+
|
|
317
|
+
## Conflicting Requirements and Conformance Test Debugging
|
|
318
|
+
|
|
319
|
+
The renderer can detect conflicting requirements. Two functional requirements may be in conflict if conformance tests for a previously passing requirement begin to fail after a new requirement is rendered. When a conformance test failure occurs, the first step is to determine **where the issue lies**. There are three possible outcomes:
|
|
320
|
+
|
|
321
|
+
1. **The implementation is incorrect** — the generated code does not correctly implement the functional spec. Fix the spec to clarify intent and re-render.
|
|
322
|
+
2. **The conformance tests are incorrect** — the generated tests do not accurately verify the spec. Adjust `***test reqs***` or `***acceptance tests***` to guide better test generation and re-render.
|
|
323
|
+
3. **The requirements conflict** — the two functional specs are inherently contradictory. One or both specs must be revised to resolve the conflict before re-rendering.
|
|
324
|
+
|
|
325
|
+
Conflicting requirements are the most costly outcome and should be **prevented proactively**. When writing or modifying functional specs, carefully consider how each spec interacts with all previous specs. If ambiguity exists, add explicit detail to the spec to eliminate any possible interpretation that could conflict with earlier requirements.
|
|
@@ -58,7 +58,9 @@
|
|
|
58
58
|
"Bash(/Users/zanjonke/Work/codeplain/codeplain/venv/bin/python -m pytest tests/test_plain_modules.py --tb=short)",
|
|
59
59
|
"Bash(/Users/zanjonke/Work/codeplain/codeplain/.venv/bin/python -m pytest tests/test_plain_modules.py --tb=short)",
|
|
60
60
|
"Bash(/Users/zanjonke/Work/codeplain/codeplain/.venv/bin/python -m pytest tests/test_partial_rendering.py tests/test_git_utils.py --tb=short)",
|
|
61
|
-
"Bash(.venv/bin/python *)"
|
|
61
|
+
"Bash(.venv/bin/python *)",
|
|
62
|
+
"Bash(python *)",
|
|
63
|
+
"Bash(python3 *)"
|
|
62
64
|
]
|
|
63
65
|
}
|
|
64
66
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
// Mirror of .vscode/settings.json adapted for Zed.
|
|
3
|
+
// See https://zed.dev/docs/configuring-zed for available options.
|
|
4
|
+
"format_on_save": "on",
|
|
5
|
+
"hard_tabs": false,
|
|
6
|
+
"auto_indent_on_paste": true,
|
|
7
|
+
"languages": {
|
|
8
|
+
"Python": {
|
|
9
|
+
"format_on_save": "on",
|
|
10
|
+
"hard_tabs": false,
|
|
11
|
+
"tab_size": 4,
|
|
12
|
+
"preferred_line_length": 120,
|
|
13
|
+
"show_wrap_guides": true,
|
|
14
|
+
"wrap_guides": [120],
|
|
15
|
+
// Pipe isort -> black in a single shell command so Zed only sees one
|
|
16
|
+
// buffer update (avoids visible flicker between the two passes).
|
|
17
|
+
// Both tools read [tool.isort] and [tool.black] from pyproject.toml,
|
|
18
|
+
// so line-length=120 etc. is honored.
|
|
19
|
+
// We intentionally do NOT enable Pyright's `source.organizeImports` code action
|
|
20
|
+
// here — it splits multi-import lines regardless of line length and ignores isort.
|
|
21
|
+
"formatter": {
|
|
22
|
+
"external": {
|
|
23
|
+
"command": "sh",
|
|
24
|
+
"arguments": [
|
|
25
|
+
"-c",
|
|
26
|
+
"isort --quiet --filename \"$1\" - | black --quiet -",
|
|
27
|
+
"sh",
|
|
28
|
+
"{buffer_path}",
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
// Pyright/Pylance extra paths (equivalent of python.analysis.extraPaths in VS Code).
|
|
35
|
+
"lsp": {
|
|
36
|
+
"pyright": {
|
|
37
|
+
"settings": {
|
|
38
|
+
"python": {
|
|
39
|
+
"analysis": {
|
|
40
|
+
"extraPaths": ["."],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
"pylsp": {
|
|
46
|
+
"settings": {
|
|
47
|
+
"pylsp": {
|
|
48
|
+
"plugins": {
|
|
49
|
+
"jedi": {
|
|
50
|
+
"extra_paths": ["."],
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
// Equivalent of VS Code's "files.associations" — treat anything under tests/data as plain text.
|
|
58
|
+
"file_types": {
|
|
59
|
+
"Plain Text": ["tests/data/**"],
|
|
60
|
+
},
|
|
61
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## What this is
|
|
6
|
+
|
|
7
|
+
`codeplain` is the **plain2code CLI client** for the Codeplain code-generation service. It converts `***plain` specifications into production-ready code: it takes a `.plain` file (Markdown + YAML frontmatter + Liquid templating) and drives the remote Codeplain REST API (`https://api.codeplain.ai`) through a hierarchical state machine to generate, unit-test, refactor, and conformance-test code on disk. Every render needs a `CODEPLAIN_API_KEY` (env var or `--api-key`).
|
|
8
|
+
|
|
9
|
+
The full `***plain` language reference lives at `.claude/docs/PLAIN_REFERENCE.md` — read it before editing any parser/spec code (`plain_file.py`, `plain_spec.py`, `plain_modules.py`) or authoring example `.plain` files.
|
|
10
|
+
|
|
11
|
+
Python 3.11 is required (pinned in `pyproject.toml`). The CLI entry point is `plain2code.py` `main()`, also installed as the `codeplain` console script.
|
|
12
|
+
|
|
13
|
+
## Common commands
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Run the renderer directly (no install)
|
|
17
|
+
python plain2code.py path/to/spec.plain
|
|
18
|
+
|
|
19
|
+
# Run an end-to-end example (Python target)
|
|
20
|
+
cd examples/example_hello_world_python && sh run.sh
|
|
21
|
+
# Or run all language examples
|
|
22
|
+
cd examples && sh run.sh
|
|
23
|
+
|
|
24
|
+
# Tests (pytest config in pytest.ini; tests/data is fixture-only and excluded)
|
|
25
|
+
pytest # full suite
|
|
26
|
+
pytest tests/test_plain_modules.py # one file
|
|
27
|
+
pytest tests/test_plainspec.py::TestX::test_y # one test
|
|
28
|
+
|
|
29
|
+
# Lint / format / typecheck (configs in pyproject.toml + .flake8)
|
|
30
|
+
black .
|
|
31
|
+
isort .
|
|
32
|
+
flake8
|
|
33
|
+
mypy .
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
CLI option reference lives in `docs/plain2code_cli.md` (regenerated from `docs/generate_cli.py`).
|
|
37
|
+
|
|
38
|
+
Useful flags when iterating on rendering logic:
|
|
39
|
+
- `--dry-run` — parse the plain file, compute the render range, print what would render. No API calls.
|
|
40
|
+
- `--full-plain` — print the fully-expanded plain source (after Liquid template loading).
|
|
41
|
+
- `--headless` — no Textual TUI; logs go to file only. Easier for debugging in non-TTY contexts.
|
|
42
|
+
- `--render-machine-graph` — emit `render_machine_diagram.png` of the state machine.
|
|
43
|
+
- `--render-range` / `--render-from` — re-render a subset of FRIDs (functional-requirement IDs).
|
|
44
|
+
- `--force-render` — re-render required modules even if their hash is unchanged.
|
|
45
|
+
|
|
46
|
+
Default folders (configurable): `plain_modules/` (build), `conformance_tests/`, `dist/` (after `--copy-build`, which is on by default).
|
|
47
|
+
|
|
48
|
+
## Architecture
|
|
49
|
+
|
|
50
|
+
### Pipeline overview
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
.plain file
|
|
54
|
+
└─ plain_file.py parse (frontmatter + mistletoe + liquid2)
|
|
55
|
+
└─ plain_spec.py normalise sections (definitions / implementation reqs / test reqs / functional specs)
|
|
56
|
+
└─ plain_modules.py PlainModule tree (recursive `requires:` graph)
|
|
57
|
+
│
|
|
58
|
+
▼
|
|
59
|
+
plain2code.py main → render() builds:
|
|
60
|
+
├─ codeplain_REST_api.CodeplainAPI HTTP client with retry + typed exceptions
|
|
61
|
+
├─ partial_rendering.detect_partial_rendering inspects prior state to offer "continue/restart/re-render"
|
|
62
|
+
└─ ModuleRenderer (module_renderer.py)
|
|
63
|
+
└─ for each module bottom-up:
|
|
64
|
+
CodeRenderer (render_machine/code_renderer.py)
|
|
65
|
+
└─ HierarchicalGraphMachine (transitions library) driven by RenderContext
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### State machine (`render_machine/`)
|
|
69
|
+
|
|
70
|
+
`code_renderer.py` runs a single loop: read current state → execute the mapped `Action` → dispatch the resulting trigger → repeat until `RENDER_COMPLETED` / `RENDER_FAILED`. The whole topology lives in **one place**: `render_machine/state_machine_config.py` (`StateMachineConfig.get_states` / `get_transitions` / `get_action_map` / `get_action_result_triggers_map`). State name constants are in `render_machine/states.py` (camelCase is required by `transitions`); trigger names are in `render_machine/triggers.py`.
|
|
71
|
+
|
|
72
|
+
The shared mutable state for one module's render lives on `RenderContext` (`render_machine/render_context.py`) — `transitions` uses it as the model, so every `on_enter`/`on_exit` callback referenced in the state config must exist there. Concrete work happens in `render_machine/actions/*.py`; each action subclasses `BaseAction` and returns an outcome string that `get_action_result_triggers_map` maps to the next trigger.
|
|
73
|
+
|
|
74
|
+
When adding a new state or action: update `states.py`, `triggers.py`, and `state_machine_config.py` in lockstep, add the action class, and wire up any callback methods on `RenderContext`.
|
|
75
|
+
|
|
76
|
+
The high-level FRID lifecycle inside `IMPLEMENTING_FRID` is: render functional requirement → unit tests (run/fix) → commit → refactor (with its own unit-tests sub-loop) → conformance tests (generate, prepare env, run, fix, postprocess: summary → commit → ambiguity analysis) → `FRID_FULLY_IMPLEMENTED` → next FRID or `RENDER_COMPLETED`. The unit-tests sub-state is reused in three contexts via `UnitTestsConfig.for_implementation/for_refactoring/for_conformance_tests`.
|
|
77
|
+
|
|
78
|
+
### Modules, FRIDs, and git as the persistence layer
|
|
79
|
+
|
|
80
|
+
- A "module" is one `.plain` file. Modules can `requires:` other modules in their frontmatter, forming a DAG. `PlainModule.required_modules` is built recursively at construction time.
|
|
81
|
+
- `ModuleRenderer._render_module` renders required modules first; a module is skipped only if its plain spec hash and required-modules code hash are unchanged (see `module_metadata.json` under `<module>/.codeplain/`).
|
|
82
|
+
- Each module's build folder is its **own git repo**. Each functional requirement is committed with a marker message (`git_utils.FUNCTIONAL_REQUIREMENT_FINISHED_COMMIT_MESSAGE` etc.) and a FRID, which is how `--render-from` / `--render-range` / partial rendering locate prior progress. Don't bypass `git_utils.has_commit_for_frid` — `MissingPreviousFunctionalitiesError` exists to force the user to render predecessors first.
|
|
83
|
+
|
|
84
|
+
### Partial rendering
|
|
85
|
+
|
|
86
|
+
`partial_rendering.py` decides what to offer the user when a build folder already exists:
|
|
87
|
+
- `detect_partial_rendering` finds the last rendered module/FRID and checks for spec hash drift (`spec_change`) or required-module code drift (`code_change`).
|
|
88
|
+
- `get_choices` builds the menu (continue / re-render affected / re-render from first / quit).
|
|
89
|
+
- If there are >2 choices, `tui/partial_render_tui.py` shows them; otherwise the single actionable choice is taken automatically. `--render-range` / `--render-from` skip this step entirely.
|
|
90
|
+
|
|
91
|
+
### TUI and threading
|
|
92
|
+
|
|
93
|
+
When not `--headless`, `plain2code.render()` starts the renderer on a daemon thread and runs `tui/plain2code_tui.Plain2CodeTUI` (Textual app) on the main thread. They communicate via `event_bus.EventBus` (events defined in `plain2code_events.py`). `RunState` (`plain2code_state.py`) is the shared run-level state object passed everywhere. `stop_event` and `enter_pause_event` (threading.Events) propagate cancel/pause from the TUI down into `CodeRenderer.run`.
|
|
94
|
+
|
|
95
|
+
### Templates and the standard library
|
|
96
|
+
|
|
97
|
+
`standard_template_library/` ships built-in `.plain` boilerplate (`python-console-app-template.plain`, `golang-console-app-template.plain`, `typescript-react-app-template.plain`, `typescript-react-app-boilerplate.plain`). Resolution order in `file_utils.get_template_directories`: 1) directory of the user's plain file, 2) `--template-dir`, 3) `standard_template_library/`. The ship list in `pyproject.toml` (`tool.hatch.build.targets.wheel.include`) controls what gets packaged — keep it in sync when adding new top-level dirs that need to be in the wheel (`config/`, `render_machine/`, `standard_template_library/`, `tui/` are currently included).
|
|
98
|
+
|
|
99
|
+
### Test scripts
|
|
100
|
+
|
|
101
|
+
`test_scripts/` holds shell scripts (`run_unittests_python.sh`, `run_conformance_tests_golang.sh`, etc.) that are passed via `--unittests-script` / `--conformance-tests-script` (paths can be set via config.yaml relative to the config file, or absolute). Conformance-test execution is opt-in: it's only enabled when `--conformance-tests-script` is provided (`args.render_conformance_tests` is set in `parse_arguments`).
|
|
102
|
+
|
|
103
|
+
### Configuration files
|
|
104
|
+
|
|
105
|
+
`config.yaml` (or whatever `--config-name` says) is auto-discovered next to the plain file or in CWD; CLI args override it. See `plain2code_arguments.update_args_with_config` for precedence rules — boolean store-true flags only override config when explicitly passed. `system_config.py` is unrelated: it loads the bundled `config/system_config.yaml` for client version + canned error messages.
|
|
106
|
+
|
|
107
|
+
## Things worth knowing before editing
|
|
108
|
+
|
|
109
|
+
- `_version.py` is the single source of truth for the client version (read by `system_config.py`). The server validates it on `connection_check`; bumping is intentional.
|
|
110
|
+
- Several mypy error codes are globally disabled in `pyproject.toml` (`var-annotated`, `assignment`, `return-value`, etc.). Don't assume mypy will catch typing issues — it's run in a permissive mode.
|
|
111
|
+
- `flake8` `max-complexity=15` is enforced; `# noqa: C901` is used on top-level orchestrators (`plain2code.main`, `render`, etc.) — that's deliberate.
|
|
112
|
+
- `tests/conftest.py` sets dummy `MASTER_KEY` / `GOOGLE_API_KEY` env vars at import time, and `tests/data` is excluded from collection — fixture files there will not be picked up as tests.
|
|
113
|
+
- Examples write into their own folder (`examples/**/build*/`, `plain_modules/`, etc.) and those are gitignored. Re-running `sh run.sh` will re-render against the existing state via partial-rendering logic.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codeplain
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Transform plain language specifications into working code
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Classifier: Environment :: Console
|
|
@@ -8,13 +8,13 @@ Classifier: Intended Audience :: Developers
|
|
|
8
8
|
Classifier: Operating System :: OS Independent
|
|
9
9
|
Classifier: Topic :: Software Development :: Code Generators
|
|
10
10
|
Requires-Python: ==3.11
|
|
11
|
-
Requires-Dist: gitpython==3.1.
|
|
11
|
+
Requires-Dist: gitpython==3.1.50
|
|
12
12
|
Requires-Dist: mistletoe==1.3.0
|
|
13
13
|
Requires-Dist: networkx==3.6.1
|
|
14
14
|
Requires-Dist: python-frontmatter==1.1.0
|
|
15
15
|
Requires-Dist: python-liquid2==0.3.0
|
|
16
|
-
Requires-Dist: pyyaml==6.0.
|
|
17
|
-
Requires-Dist: requests==2.
|
|
16
|
+
Requires-Dist: pyyaml==6.0.3
|
|
17
|
+
Requires-Dist: requests==2.34.0
|
|
18
18
|
Requires-Dist: rich==14.2.0
|
|
19
19
|
Requires-Dist: textual>=7.5.0
|
|
20
20
|
Requires-Dist: tiktoken==0.12.0
|
|
@@ -115,7 +115,7 @@ After completing the installation steps above, you can immediately test the syst
|
|
|
115
115
|
|
|
116
116
|
### Documentation
|
|
117
117
|
|
|
118
|
-
- For more details on the ***plain format, see the [***plain language specification](https://www.plainlang.org/docs/
|
|
118
|
+
- For more details on the ***plain format, see the [***plain language specification](https://www.plainlang.org/docs/).
|
|
119
119
|
- For step-by-step instructions for creating your first ***plain project see the [Kickstart your ***plain project](docs/starting_a_plain_project_from_scratch.md).
|
|
120
120
|
- For complete CLI documentation and usage examples, see [plain2code CLI documentation](docs/plain2code_cli.md).
|
|
121
121
|
|
|
@@ -86,7 +86,7 @@ After completing the installation steps above, you can immediately test the syst
|
|
|
86
86
|
|
|
87
87
|
### Documentation
|
|
88
88
|
|
|
89
|
-
- For more details on the ***plain format, see the [***plain language specification](https://www.plainlang.org/docs/
|
|
89
|
+
- For more details on the ***plain format, see the [***plain language specification](https://www.plainlang.org/docs/).
|
|
90
90
|
- For step-by-step instructions for creating your first ***plain project see the [Kickstart your ***plain project](docs/starting_a_plain_project_from_scratch.md).
|
|
91
91
|
- For complete CLI documentation and usage examples, see [plain2code CLI documentation](docs/plain2code_cli.md).
|
|
92
92
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.0"
|