microcoreos 0.4.2__tar.gz → 0.4.4__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 (209) hide show
  1. {microcoreos-0.4.2 → microcoreos-0.4.4}/AI_CONTEXT.md +1 -1
  2. {microcoreos-0.4.2 → microcoreos-0.4.4}/PKG-INFO +3 -2
  3. {microcoreos-0.4.2 → microcoreos-0.4.4}/README.md +2 -1
  4. {microcoreos-0.4.2 → microcoreos-0.4.4}/docs/CLI.md +11 -1
  5. {microcoreos-0.4.2 → microcoreos-0.4.4}/domains/system/plugins/system_logs_stream_plugin.py +8 -2
  6. {microcoreos-0.4.2 → microcoreos-0.4.4}/microcoreos_dev/cli.py +2 -0
  7. {microcoreos-0.4.2 → microcoreos-0.4.4}/microcoreos_dev/pipeline.py +37 -5
  8. {microcoreos-0.4.2 → microcoreos-0.4.4}/microcoreos_dev/plan/__init__.py +11 -0
  9. {microcoreos-0.4.2 → microcoreos-0.4.4}/microcoreos_dev/plan/rules.py +7 -1
  10. {microcoreos-0.4.2 → microcoreos-0.4.4}/microcoreos_dev/plan/scan.py +26 -14
  11. {microcoreos-0.4.2 → microcoreos-0.4.4}/microcoreos_dev/plan/schema.py +45 -3
  12. microcoreos-0.4.4/microcoreos_dev/plan/sync.py +140 -0
  13. {microcoreos-0.4.2 → microcoreos-0.4.4}/pyproject.toml +1 -1
  14. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/dev/test_pipeline.py +46 -0
  15. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/dev/test_plan_validator.py +242 -0
  16. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/tools/http_server/test_http_server_tool.py +3 -2
  17. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/tools/logger/test_logger_tool.py +26 -0
  18. {microcoreos-0.4.2 → microcoreos-0.4.4}/tools/logger/logger_tool.py +19 -3
  19. {microcoreos-0.4.2 → microcoreos-0.4.4}/uv.lock +1 -1
  20. {microcoreos-0.4.2 → microcoreos-0.4.4}/.agent/skills/microcoreos-architecture/SKILL.md +0 -0
  21. {microcoreos-0.4.2 → microcoreos-0.4.4}/.agent/skills/microcoreos-architecture/agent.md +0 -0
  22. {microcoreos-0.4.2 → microcoreos-0.4.4}/.agent/workflows/feature-plan.md +0 -0
  23. {microcoreos-0.4.2 → microcoreos-0.4.4}/.agent/workflows/multi-domain-plan.md +0 -0
  24. {microcoreos-0.4.2 → microcoreos-0.4.4}/.agent/workflows/new-domain.md +0 -0
  25. {microcoreos-0.4.2 → microcoreos-0.4.4}/.agent/workflows/new-tool.md +0 -0
  26. {microcoreos-0.4.2 → microcoreos-0.4.4}/.agent/workflows/solo-build.md +0 -0
  27. {microcoreos-0.4.2 → microcoreos-0.4.4}/.claude/settings.local.json +0 -0
  28. {microcoreos-0.4.2 → microcoreos-0.4.4}/.dockerignore +0 -0
  29. {microcoreos-0.4.2 → microcoreos-0.4.4}/.env.example +0 -0
  30. {microcoreos-0.4.2 → microcoreos-0.4.4}/.github/workflows/ci.yml +0 -0
  31. {microcoreos-0.4.2 → microcoreos-0.4.4}/.github/workflows/release.yml +0 -0
  32. {microcoreos-0.4.2 → microcoreos-0.4.4}/.gitignore +0 -0
  33. {microcoreos-0.4.2 → microcoreos-0.4.4}/.mutmut-cache +0 -0
  34. {microcoreos-0.4.2 → microcoreos-0.4.4}/.python-version +0 -0
  35. {microcoreos-0.4.2 → microcoreos-0.4.4}/AGENTS.md +0 -0
  36. {microcoreos-0.4.2 → microcoreos-0.4.4}/Dockerfile +0 -0
  37. {microcoreos-0.4.2 → microcoreos-0.4.4}/INSTRUCTIONS_FOR_AI.md +0 -0
  38. {microcoreos-0.4.2 → microcoreos-0.4.4}/LICENSE +0 -0
  39. {microcoreos-0.4.2 → microcoreos-0.4.4}/ROADMAP.md +0 -0
  40. {microcoreos-0.4.2 → microcoreos-0.4.4}/cli.py +0 -0
  41. {microcoreos-0.4.2 → microcoreos-0.4.4}/conftest.py +0 -0
  42. {microcoreos-0.4.2 → microcoreos-0.4.4}/dev_infra/cache_probe.py +0 -0
  43. {microcoreos-0.4.2 → microcoreos-0.4.4}/dev_infra/docker-compose.yml +0 -0
  44. {microcoreos-0.4.2 → microcoreos-0.4.4}/docs/CORE_INFRASTRUCTURE.md +0 -0
  45. {microcoreos-0.4.2 → microcoreos-0.4.4}/docs/ELASTIC_DEPLOYMENT.md +0 -0
  46. {microcoreos-0.4.2 → microcoreos-0.4.4}/docs/EVENT_BUS.md +0 -0
  47. {microcoreos-0.4.2 → microcoreos-0.4.4}/docs/HTTP_SERVER.md +0 -0
  48. {microcoreos-0.4.2 → microcoreos-0.4.4}/docs/INDEX.md +0 -0
  49. {microcoreos-0.4.2 → microcoreos-0.4.4}/docs/OBSERVABILITY.md +0 -0
  50. {microcoreos-0.4.2 → microcoreos-0.4.4}/docs/OBSERVABILITY_API.md +0 -0
  51. {microcoreos-0.4.2 → microcoreos-0.4.4}/docs/PARALLEL_DEVELOPMENT.md +0 -0
  52. {microcoreos-0.4.2 → microcoreos-0.4.4}/docs/translations/es/README.md +0 -0
  53. {microcoreos-0.4.2 → microcoreos-0.4.4}/domains/devtools/lint/plugin_sources.py +0 -0
  54. {microcoreos-0.4.2 → microcoreos-0.4.4}/domains/devtools/plugins/discovery_naming_linter_plugin.py +0 -0
  55. {microcoreos-0.4.2 → microcoreos-0.4.4}/domains/devtools/plugins/domain_isolation_linter_plugin.py +0 -0
  56. {microcoreos-0.4.2 → microcoreos-0.4.4}/domains/devtools/plugins/event_contract_linter_plugin.py +0 -0
  57. {microcoreos-0.4.2 → microcoreos-0.4.4}/domains/devtools/plugins/event_schemas_plugin.py +0 -0
  58. {microcoreos-0.4.2 → microcoreos-0.4.4}/domains/devtools/plugins/field_divergence_linter_plugin.py +0 -0
  59. {microcoreos-0.4.2 → microcoreos-0.4.4}/domains/devtools/plugins/route_collision_linter_plugin.py +0 -0
  60. {microcoreos-0.4.2 → microcoreos-0.4.4}/domains/devtools/plugins/table_ownership_linter_plugin.py +0 -0
  61. {microcoreos-0.4.2 → microcoreos-0.4.4}/domains/devtools/plugins/tool_doc_drift_linter_plugin.py +0 -0
  62. {microcoreos-0.4.2 → microcoreos-0.4.4}/domains/system/plugins/event_delivery_monitor_plugin.py +0 -0
  63. {microcoreos-0.4.2 → microcoreos-0.4.4}/domains/system/plugins/system_events_plugin.py +0 -0
  64. {microcoreos-0.4.2 → microcoreos-0.4.4}/domains/system/plugins/system_events_stream_plugin.py +0 -0
  65. {microcoreos-0.4.2 → microcoreos-0.4.4}/domains/system/plugins/system_metrics_plugin.py +0 -0
  66. {microcoreos-0.4.2 → microcoreos-0.4.4}/domains/system/plugins/system_status_plugin.py +0 -0
  67. {microcoreos-0.4.2 → microcoreos-0.4.4}/domains/system/plugins/system_traces_plugin.py +0 -0
  68. {microcoreos-0.4.2 → microcoreos-0.4.4}/domains/system/plugins/system_traces_stream_plugin.py +0 -0
  69. {microcoreos-0.4.2 → microcoreos-0.4.4}/domains/system/plugins/tool_health_plugin.py +0 -0
  70. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_domains/chaos/plugins/blocking_boot_plugin.py +0 -0
  71. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_domains/chaos/plugins/chaos_control_plugin.py +0 -0
  72. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_domains/chaos/plugins/failing_plugin.py +0 -0
  73. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_domains/chaos/plugins/stress_plugin.py +0 -0
  74. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_domains/ping/plugins/ping_plugin.py +0 -0
  75. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_domains/scheduler/migrations/001_scheduler_one_shots.sql +0 -0
  76. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_domains/scheduler/models/scheduler_one_shot.py +0 -0
  77. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_domains/scheduler/plugins/durable_one_shots_plugin.py +0 -0
  78. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_domains/users/migrations/001_create_users.sql +0 -0
  79. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_domains/users/models/user.py +0 -0
  80. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_domains/users/plugins/create_user_plugin.py +0 -0
  81. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_domains/users/plugins/delete_user_plugin.py +0 -0
  82. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_domains/users/plugins/get_me_plugin.py +0 -0
  83. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_domains/users/plugins/get_user_by_id_plugin.py +0 -0
  84. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_domains/users/plugins/get_users_plugin.py +0 -0
  85. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_domains/users/plugins/login_plugin.py +0 -0
  86. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_domains/users/plugins/logout_plugin.py +0 -0
  87. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_domains/users/plugins/update_user_plugin.py +0 -0
  88. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_domains/users/plugins/welcome_service_plugin.py +0 -0
  89. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_tools/auth/auth_tool.py +0 -0
  90. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_tools/auth/tests/auth_tool_test.py +0 -0
  91. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_tools/chaos/chaos_tool.py +0 -0
  92. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_tools/kafka/kafka_driver.py +0 -0
  93. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_tools/postgresql/postgresql_tool.py +0 -0
  94. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_tools/postgresql/tests/postgresql_describe_schema_test.py +0 -0
  95. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_tools/postgresql/tests/postgresql_tool_test.py +0 -0
  96. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_tools/rabbitmq/rabbitmq_driver.py +0 -0
  97. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_tools/redis_state/redis_state_tool.py +0 -0
  98. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_tools/s3/__init__.py +0 -0
  99. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_tools/s3/s3_tool.py +0 -0
  100. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_tools/s3/tests/s3_tool_test.py +0 -0
  101. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_tools/scheduler/scheduler_tool.py +0 -0
  102. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_tools/scheduler/tests/scheduler_singleton_test.py +0 -0
  103. {microcoreos-0.4.2 → microcoreos-0.4.4}/extras/available_tools/scheduler/tests/scheduler_tool_test.py +0 -0
  104. {microcoreos-0.4.2 → microcoreos-0.4.4}/hatch_build.py +0 -0
  105. {microcoreos-0.4.2 → microcoreos-0.4.4}/main.py +0 -0
  106. {microcoreos-0.4.2 → microcoreos-0.4.4}/microcoreos/__init__.py +0 -0
  107. {microcoreos-0.4.2 → microcoreos-0.4.4}/microcoreos/base_plugin.py +0 -0
  108. {microcoreos-0.4.2 → microcoreos-0.4.4}/microcoreos/base_tool.py +0 -0
  109. {microcoreos-0.4.2 → microcoreos-0.4.4}/microcoreos/catalog.py +0 -0
  110. {microcoreos-0.4.2 → microcoreos-0.4.4}/microcoreos/cli.py +0 -0
  111. {microcoreos-0.4.2 → microcoreos-0.4.4}/microcoreos/container.py +0 -0
  112. {microcoreos-0.4.2 → microcoreos-0.4.4}/microcoreos/context.py +0 -0
  113. {microcoreos-0.4.2 → microcoreos-0.4.4}/microcoreos/kernel.py +0 -0
  114. {microcoreos-0.4.2 → microcoreos-0.4.4}/microcoreos/project.py +0 -0
  115. {microcoreos-0.4.2 → microcoreos-0.4.4}/microcoreos/project_readme.md +0 -0
  116. {microcoreos-0.4.2 → microcoreos-0.4.4}/microcoreos/registry.py +0 -0
  117. {microcoreos-0.4.2 → microcoreos-0.4.4}/microcoreos/scaffold.py +0 -0
  118. {microcoreos-0.4.2 → microcoreos-0.4.4}/microcoreos/upgrade.py +0 -0
  119. {microcoreos-0.4.2 → microcoreos-0.4.4}/microcoreos_dev/__init__.py +0 -0
  120. {microcoreos-0.4.2 → microcoreos-0.4.4}/microcoreos_dev/fuzzer.py +0 -0
  121. {microcoreos-0.4.2 → microcoreos-0.4.4}/microcoreos_dev/probe.py +0 -0
  122. {microcoreos-0.4.2 → microcoreos-0.4.4}/plans/README.md +0 -0
  123. {microcoreos-0.4.2 → microcoreos-0.4.4}/plans/active_plan.md +0 -0
  124. {microcoreos-0.4.2 → microcoreos-0.4.4}/plans/active_plan.yaml +0 -0
  125. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/core/test_core.py +0 -0
  126. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/core/test_core_purity.py +0 -0
  127. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/core/test_kernel.py +0 -0
  128. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/core/test_no_retry.py +0 -0
  129. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/core/test_plugin_di_fixtures.py +0 -0
  130. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/core/test_registry_collisions.py +0 -0
  131. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/core/test_tool_proxy.py +0 -0
  132. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/dev/corpus/README.md +0 -0
  133. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/dev/corpus/qwen_twitter_plan.yaml +0 -0
  134. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/domains/ping/test_ping_plugin.py +0 -0
  135. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/domains/users/test_create_user_plugin.py +0 -0
  136. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/domains/users/test_delete_user_plugin.py +0 -0
  137. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/domains/users/test_get_me_plugin.py +0 -0
  138. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/domains/users/test_get_user_by_id_plugin.py +0 -0
  139. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/domains/users/test_get_users_plugin.py +0 -0
  140. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/domains/users/test_login_plugin.py +0 -0
  141. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/domains/users/test_logout_plugin.py +0 -0
  142. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/domains/users/test_update_user_plugin.py +0 -0
  143. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/domains/users/test_welcome_service_plugin.py +0 -0
  144. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/helpers/active_db.py +0 -0
  145. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/helpers/async_wait.py +0 -0
  146. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/helpers/mock_db.py +0 -0
  147. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/helpers/trace_chains.py +0 -0
  148. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/linters/test_discovery_naming_linter.py +0 -0
  149. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/linters/test_domain_isolation_linter.py +0 -0
  150. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/linters/test_event_contract_linter.py +0 -0
  151. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/linters/test_field_divergence_linter.py +0 -0
  152. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/linters/test_route_collision_linter.py +0 -0
  153. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/linters/test_table_ownership_linter.py +0 -0
  154. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/linters/test_tool_doc_drift_linter.py +0 -0
  155. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/system/test_catalog.py +0 -0
  156. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/system/test_chaos_control.py +0 -0
  157. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/system/test_chaos_tool.py +0 -0
  158. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/system/test_cli.py +0 -0
  159. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/system/test_durable_one_shots.py +0 -0
  160. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/system/test_event_schemas_plugin.py +0 -0
  161. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/system/test_scaffold.py +0 -0
  162. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/system/test_system_events_stats.py +0 -0
  163. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/system/test_system_traces_plugin.py +0 -0
  164. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/system/test_trace_chain_helper.py +0 -0
  165. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/system/test_upgrade.py +0 -0
  166. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/tools/config/test_config_tool.py +0 -0
  167. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/tools/context/test_context_tool.py +0 -0
  168. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/tools/db/test_db_parity.py +0 -0
  169. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/tools/event_bus/test_event_bus_broker_parity.py +0 -0
  170. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/tools/event_bus/test_event_bus_groups.py +0 -0
  171. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/tools/event_bus/test_event_bus_kafka_parity.py +0 -0
  172. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/tools/event_bus/test_event_bus_rabbitmq_parity.py +0 -0
  173. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/tools/event_bus/test_event_bus_tool.py +0 -0
  174. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/tools/event_bus/test_redis_streams_driver.py +0 -0
  175. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/tools/http_server/test_http_params_hardening.py +0 -0
  176. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/tools/http_server/test_http_pipeline.py +0 -0
  177. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/tools/http_server/test_security_hardening.py +0 -0
  178. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/tools/registry/test_registry_tool.py +0 -0
  179. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/tools/s3/test_s3_parity.py +0 -0
  180. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/tools/sqlite/test_sqlite_concurrency.py +0 -0
  181. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/tools/sqlite/test_sqlite_describe_schema.py +0 -0
  182. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/tools/sqlite/test_sqlite_driver.py +0 -0
  183. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/tools/sqlite/test_sqlite_migrations.py +0 -0
  184. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/tools/sqlite/test_sqlite_tool.py +0 -0
  185. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/tools/state/test_state_parity.py +0 -0
  186. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/tools/state/test_state_tool.py +0 -0
  187. {microcoreos-0.4.2 → microcoreos-0.4.4}/tests/tools/telemetry/test_telemetry_tool.py +0 -0
  188. {microcoreos-0.4.2 → microcoreos-0.4.4}/tools/config/config_tool.py +0 -0
  189. {microcoreos-0.4.2 → microcoreos-0.4.4}/tools/context/authoring_guide.md +0 -0
  190. {microcoreos-0.4.2 → microcoreos-0.4.4}/tools/context/context_tool.py +0 -0
  191. {microcoreos-0.4.2 → microcoreos-0.4.4}/tools/context/renderers.py +0 -0
  192. {microcoreos-0.4.2 → microcoreos-0.4.4}/tools/context/scanners.py +0 -0
  193. {microcoreos-0.4.2 → microcoreos-0.4.4}/tools/event_bus/drivers.py +0 -0
  194. {microcoreos-0.4.2 → microcoreos-0.4.4}/tools/event_bus/envelope.py +0 -0
  195. {microcoreos-0.4.2 → microcoreos-0.4.4}/tools/event_bus/event_bus_tool.py +0 -0
  196. {microcoreos-0.4.2 → microcoreos-0.4.4}/tools/event_bus/redis_streams_driver.py +0 -0
  197. {microcoreos-0.4.2 → microcoreos-0.4.4}/tools/event_bus/sqlite_driver.py +0 -0
  198. {microcoreos-0.4.2 → microcoreos-0.4.4}/tools/http_server/context.py +0 -0
  199. {microcoreos-0.4.2 → microcoreos-0.4.4}/tools/http_server/http_server_tool.py +0 -0
  200. {microcoreos-0.4.2 → microcoreos-0.4.4}/tools/http_server/pipeline.py +0 -0
  201. {microcoreos-0.4.2 → microcoreos-0.4.4}/tools/http_server/types.py +0 -0
  202. {microcoreos-0.4.2 → microcoreos-0.4.4}/tools/sqlite/errors.py +0 -0
  203. {microcoreos-0.4.2 → microcoreos-0.4.4}/tools/sqlite/migrations.py +0 -0
  204. {microcoreos-0.4.2 → microcoreos-0.4.4}/tools/sqlite/sqlite_tool.py +0 -0
  205. {microcoreos-0.4.2 → microcoreos-0.4.4}/tools/sqlite/transaction.py +0 -0
  206. {microcoreos-0.4.2 → microcoreos-0.4.4}/tools/state/state_tool.py +0 -0
  207. {microcoreos-0.4.2 → microcoreos-0.4.4}/tools/system/registry_tool.py +0 -0
  208. {microcoreos-0.4.2 → microcoreos-0.4.4}/tools/telemetry/__init__.py +0 -0
  209. {microcoreos-0.4.2 → microcoreos-0.4.4}/tools/telemetry/telemetry_tool.py +0 -0
@@ -321,7 +321,7 @@ Async SQLite Persistence Tool (sqlite):
321
321
  - **Events emitted**: none
322
322
  - **Events consumed**: none
323
323
  - **Dependencies**: container, http, logger
324
- - **Plugins**: devtools.DiscoveryNamingLinterPlugin, devtools.DocPathLinterPlugin, devtools.DomainIsolationLinterPlugin, devtools.EventContractLinterPlugin, devtools.EventSchemasPlugin, devtools.FieldDivergenceLinterPlugin, devtools.RouteCollisionLinterPlugin, devtools.TableOwnershipLinterPlugin, devtools.ToolDocDriftLinterPlugin
324
+ - **Plugins**: devtools.DiscoveryNamingLinterPlugin, devtools.DomainIsolationLinterPlugin, devtools.EventContractLinterPlugin, devtools.EventSchemasPlugin, devtools.FieldDivergenceLinterPlugin, devtools.RouteCollisionLinterPlugin, devtools.TableOwnershipLinterPlugin, devtools.ToolDocDriftLinterPlugin
325
325
 
326
326
  ### `system`
327
327
  - **Tables**: none
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: microcoreos
3
- Version: 0.4.2
3
+ Version: 0.4.4
4
4
  Summary: Atomic Microkernel Architecture optimized for AI-Driven Development
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -124,7 +124,8 @@ Use that rather than `uv add 'microcoreos[auth]'`. The extra is only the **depen
124
124
  | Command | Purpose |
125
125
  |---|---|
126
126
  | `microcoreos status` | Reports active plan progress and `AI_CONTEXT.md` freshness |
127
- | `microcoreos plan validate` | Validates active plan YAML (`plans/active_plan.yaml`) offline against 18 rules |
127
+ | `microcoreos plan validate` | Validates active plan YAML (`plans/active_plan.yaml`) offline against 19 rules (`--fix` to sync checklist first) |
128
+ | `microcoreos plan sync` | Generates or updates execution checklist (`plans/active_plan.md`) preserving progress |
128
129
  | `microcoreos migrate` | Applies SQL migrations AND regenerates `AI_CONTEXT.md` (the AI context manifest) |
129
130
  | `microcoreos schema` | Inspects live database schema and normalized tables |
130
131
 
@@ -83,7 +83,8 @@ Use that rather than `uv add 'microcoreos[auth]'`. The extra is only the **depen
83
83
  | Command | Purpose |
84
84
  |---|---|
85
85
  | `microcoreos status` | Reports active plan progress and `AI_CONTEXT.md` freshness |
86
- | `microcoreos plan validate` | Validates active plan YAML (`plans/active_plan.yaml`) offline against 18 rules |
86
+ | `microcoreos plan validate` | Validates active plan YAML (`plans/active_plan.yaml`) offline against 19 rules (`--fix` to sync checklist first) |
87
+ | `microcoreos plan sync` | Generates or updates execution checklist (`plans/active_plan.md`) preserving progress |
87
88
  | `microcoreos migrate` | Applies SQL migrations AND regenerates `AI_CONTEXT.md` (the AI context manifest) |
88
89
  | `microcoreos schema` | Inspects live database schema and normalized tables |
89
90
 
@@ -11,12 +11,14 @@ microcoreos [run] [--boot-tool <tool>] Boot the Kernel
11
11
  microcoreos dev Boot with auto-reload
12
12
 
13
13
  microcoreos status Active plan, progress, manifest age
14
- microcoreos plan validate [path] The 18 plan rules, offline
14
+ microcoreos plan validate [path] [--fix] The 18 plan rules, offline
15
+ microcoreos plan sync [path] Sync execution checklist from plan
15
16
  microcoreos plan probe [path] What each feature actually touches
16
17
  microcoreos migrate Migrations + regenerate AI_CONTEXT.md
17
18
  microcoreos schema The live tables and columns
18
19
  ```
19
20
 
21
+
20
22
  **Every example below needs a `uv run` prefix** (or an activated venv): the
21
23
  console script lives in `.venv/bin`, so a bare `microcoreos` is "command not
22
24
  found". And `<angle brackets>` mark a placeholder — type the value, not the
@@ -203,6 +205,8 @@ observed failing.
203
205
  ```bash
204
206
  microcoreos status # before anything else
205
207
  microcoreos plan validate # defaults to plans/active_plan.yaml
208
+ microcoreos plan validate --fix # sync checklist then validate
209
+ microcoreos plan sync # generate / sync plans/active_plan.md from plan
206
210
  microcoreos plan validate draft.yaml # or any path
207
211
  microcoreos migrate # after writing phase 0
208
212
  microcoreos schema # verify what landed in the database
@@ -216,6 +220,12 @@ and any loose `.py` in the project root — every deliverable has a declared hom
216
220
  under `domains/`, `tools/` or `tests/`, so one at the root is an agent's
217
221
  scratch file left behind. Reported, never deleted.
218
222
 
223
+ **`plan sync`** — generates or updates `plans/active_plan.md` directly from
224
+ `plans/active_plan.yaml`, preserving existing checkmarks `[x]` while generating
225
+ exact task paths for Phase 0, Phase 2 and Phase 3. Eliminates manual checklist
226
+ authoring and Rule 15 mismatches.
227
+
228
+
219
229
  **`plan probe`** — the other half of the question. `validate` asks whether the
220
230
  PLAN is well formed; this drives every feature with recording stand-ins for its
221
231
  tools and prints every call it makes.
@@ -22,10 +22,16 @@ class SystemLogsStreamPlugin(BasePlugin):
22
22
  tags=["System"],
23
23
  )
24
24
 
25
- def _on_log(self, level: str, message: str, timestamp: str, identity: str):
25
+ def _on_log(self, level: str, message: str, timestamp: str, identity: str, trace_id: str = None):
26
26
  if not self._queues:
27
27
  return
28
- record = {"level": level, "message": message, "timestamp": timestamp, "identity": identity}
28
+ record = {
29
+ "level": level,
30
+ "message": message,
31
+ "timestamp": timestamp,
32
+ "identity": identity,
33
+ "trace_id": trace_id,
34
+ }
29
35
  try:
30
36
  loop = asyncio.get_running_loop()
31
37
  for q in list(self._queues):
@@ -34,6 +34,7 @@ USAGE = """MicroCoreOS — development tooling
34
34
  Usage:
35
35
  microcoreos-dev status Active plan, progress, manifest age
36
36
  microcoreos-dev plan validate [path] Validate the plan offline (no server)
37
+ microcoreos-dev plan sync [path] Sync execution checklist from plan
37
38
  microcoreos-dev plan probe [path] Drive each feature, report what it touches
38
39
  microcoreos-dev migrate Apply migrations + regenerate AI_CONTEXT.md
39
40
  microcoreos-dev schema Print the live tables and columns
@@ -44,6 +45,7 @@ project (the directory holding tools/, domains/ and plans/).
44
45
  """
45
46
 
46
47
 
48
+
47
49
  def dispatch(name: str, argv: list[str]) -> int:
48
50
  """Run one pipeline command by name — the seam `microcoreos` calls.
49
51
 
@@ -285,27 +285,59 @@ def schema(argv: list[str]) -> int:
285
285
 
286
286
  # ── plan validate ────────────────────────────────────────────────────────────
287
287
 
288
- PLAN_USAGE = """Usage: microcoreos plan <validate|probe> [path]
288
+ PLAN_USAGE = """Usage: microcoreos plan <validate|probe|sync> [path] [--fix]
289
289
 
290
- validate the 19 rules, offline — is the PLAN well formed
290
+ validate the 19 rules, offline — is the PLAN well formed (--fix to sync checklist first)
291
291
  probe drive each feature and record what it touches — does the CODE
292
292
  match the plan it was written from
293
+ sync generate or sync plans/active_plan.md from the active plan
293
294
 
294
295
  path defaults to plans/active_plan.yaml
295
296
  """
296
297
 
297
298
 
298
299
  def plan(argv: list[str]) -> int:
299
- if not argv or argv[0] not in ("validate", "probe"):
300
+ if not argv or argv[0] not in ("validate", "probe", "sync"):
300
301
  print(PLAN_USAGE)
301
302
  return 2
302
- path = argv[1] if len(argv) > 1 else PLAN_PATH
303
- return _plan_validate(path) if argv[0] == "validate" else _run_probe(path)
303
+ cmd = argv[0]
304
+ rest = argv[1:]
305
+ fix = "--fix" in rest
306
+ args = [a for a in rest if a != "--fix"]
307
+ path = args[0] if args else PLAN_PATH
308
+
309
+ if cmd == "sync":
310
+ return _plan_sync(path)
311
+ elif cmd == "validate":
312
+ if fix:
313
+ sync_res = _plan_sync(path)
314
+ if sync_res != 0:
315
+ return sync_res
316
+ return _plan_validate(path)
317
+ else: # probe
318
+ return _run_probe(path)
319
+
320
+
321
+ def _plan_sync(path: str) -> int:
322
+ """Generate or update plans/active_plan.md from the plan file."""
323
+ root = ensure_project_on_path()
324
+ if not require_project(root):
325
+ return 2
326
+
327
+ from microcoreos_dev.plan.sync import sync_checklist
328
+
329
+ success, msg = sync_checklist(path, CHECKLIST_PATH)
330
+ if not success:
331
+ print(f"\n❌ {msg}\n")
332
+ return 1
333
+ print(f"✅ {msg}")
334
+ return 0
304
335
 
305
336
 
306
337
  def _plan_validate(path: str) -> int:
307
338
  """Run the 16 plan rules offline — no server, no jq, no curl.
308
339
 
340
+
309
341
  The validator was already pure (a Plan plus a LiveSnapshot in, violations
310
342
  out); only the snapshot needed the running system, and everything in it
311
343
  except live subscribers can be read straight off the disk. So the gate
@@ -19,8 +19,10 @@ from microcoreos_dev.plan.schema import (
19
19
  PlanLanguage,
20
20
  PlanLink,
21
21
  PlanMigration,
22
+ PlanPhase0,
22
23
  PlanPublish,
23
24
  PlanRoute,
25
+ PlanTool,
24
26
  PlanViolation,
25
27
  ValidatePlanData,
26
28
  unknown_plan_keys,
@@ -40,6 +42,10 @@ from microcoreos_dev.plan.rules import (
40
42
  run_validation,
41
43
  validate_yaml,
42
44
  )
45
+ from microcoreos_dev.plan.sync import (
46
+ generate_checklist,
47
+ sync_checklist,
48
+ )
43
49
 
44
50
  __all__ = [
45
51
  "DURABLE_DRIVERS",
@@ -51,11 +57,14 @@ __all__ = [
51
57
  "PlanLanguage",
52
58
  "PlanLink",
53
59
  "PlanMigration",
60
+ "PlanPhase0",
54
61
  "PlanPublish",
55
62
  "PlanRoute",
63
+ "PlanTool",
56
64
  "PlanValidator",
57
65
  "PlanViolation",
58
66
  "ValidatePlanData",
67
+ "generate_checklist",
59
68
  "offline_snapshot",
60
69
  "parse_plan_yaml",
61
70
  "run_validation",
@@ -63,6 +72,8 @@ __all__ = [
63
72
  "scan_live_events",
64
73
  "scan_live_routes",
65
74
  "scan_live_tables",
75
+ "sync_checklist",
66
76
  "unknown_plan_keys",
67
77
  "validate_yaml",
68
78
  ]
79
+
@@ -19,6 +19,7 @@ from microcoreos_dev.plan.schema import (
19
19
  Plan,
20
20
  PlanFeature,
21
21
  PlanLanguage,
22
+ PlanTool,
22
23
  PlanViolation,
23
24
  ValidatePlanData,
24
25
  unknown_plan_keys,
@@ -388,8 +389,13 @@ class PlanValidator:
388
389
  declared.append((flow.name, path))
389
390
  for migration in self.plan.phase_0.migrations:
390
391
  declared.append((migration.file, migration.file))
391
- for path in self.plan.phase_0.models + self.plan.phase_0.tools:
392
+ for path in self.plan.phase_0.models:
392
393
  declared.append((path, path))
394
+ for tool in self.plan.phase_0.tools:
395
+ if isinstance(tool, PlanTool):
396
+ declared.append((tool.name, tool.file))
397
+ else:
398
+ declared.append((tool, tool))
393
399
 
394
400
  def covered(path: str) -> bool:
395
401
  # substring match, path or basename — no coupling to the
@@ -208,25 +208,37 @@ def scan_live_events(domains_dir: str = "domains") -> tuple[set[str], dict[str,
208
208
  tree = ast.parse(f.read())
209
209
  except Exception:
210
210
  continue
211
+
212
+ # First pass: map each node to its enclosing ClassDef name if any
213
+ class_map = {}
211
214
  for klass in [n for n in ast.walk(tree) if isinstance(n, ast.ClassDef)]:
212
- for node in ast.walk(klass):
213
- if not (isinstance(node, ast.Call)
214
- and isinstance(node.func, ast.Attribute)
215
- and node.args
216
- and isinstance(node.args[0], ast.Constant)
217
- and isinstance(node.args[0].value, str)):
218
- continue
215
+ for child in ast.walk(klass):
216
+ class_map[child] = klass.name
217
+
218
+ for node in ast.walk(tree):
219
+ if not (isinstance(node, ast.Call)
220
+ and isinstance(node.func, ast.Attribute)):
221
+ continue
222
+ event = None
223
+ if node.args and isinstance(node.args[0], ast.Constant) and isinstance(node.args[0].value, str):
219
224
  event = node.args[0].value
220
- if node.func.attr == "publish":
221
- published.add(event)
222
- elif node.func.attr == "subscribe":
223
- handler = "handler"
224
- if len(node.args) > 1 and isinstance(node.args[1], ast.Attribute):
225
- handler = node.args[1].attr
226
- subscribers.setdefault(event, []).append(f"{klass.name}.{handler}")
225
+ for kw in node.keywords:
226
+ if kw.arg in ("event", "topic") and isinstance(kw.value, ast.Constant) and isinstance(kw.value.value, str):
227
+ event = kw.value.value
228
+ if not event:
229
+ continue
230
+ if node.func.attr == "publish":
231
+ published.add(event)
232
+ elif node.func.attr == "subscribe":
233
+ handler = "handler"
234
+ if len(node.args) > 1 and isinstance(node.args[1], ast.Attribute):
235
+ handler = node.args[1].attr
236
+ owner = class_map.get(node, filename[:-3])
237
+ subscribers.setdefault(event, []).append(f"{owner}.{handler}")
227
238
  return published, subscribers
228
239
 
229
240
 
241
+
230
242
  def offline_snapshot(domains_dir: str = "domains") -> LiveSnapshot:
231
243
  """What the repo on disk already occupies, with no system running."""
232
244
  tables = scan_live_tables(domains_dir)
@@ -4,8 +4,15 @@ Vocabulary only: what a plan may say, and which keys no field claims. The rules
4
4
  that judge a plan live in `rules.py`; what the repo on disk already occupies
5
5
  lives in `scan.py`.
6
6
  """
7
- from typing import Optional, Literal, get_args
8
- from pydantic import BaseModel, ConfigDict, Field
7
+ import re
8
+ from typing import Optional, Literal, Union, get_args
9
+ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
10
+
11
+
12
+ def payload_model_name(event: str) -> str:
13
+ """'order.paid' -> 'OrderPaidPayload' (AGENTS.md rule 10's spelling)."""
14
+ parts = [p for p in re.split(r"[^0-9A-Za-z]+", event) if p]
15
+ return ("".join(p[:1].upper() + p[1:] for p in parts) + "Payload") if parts else "EventPayload"
9
16
 
10
17
 
11
18
  class PlanRoute(BaseModel):
@@ -21,10 +28,17 @@ class PlanMigration(BaseModel):
21
28
  columns: dict[str, dict[str, str]] = {}
22
29
 
23
30
 
31
+ class PlanTool(BaseModel):
32
+ name: str
33
+ file: str
34
+ contract: list[str] = []
35
+ infra_errors: bool = False
36
+
37
+
24
38
  class PlanPhase0(BaseModel):
25
39
  migrations: list[PlanMigration] = []
26
40
  models: list[str] = []
27
- tools: list[str] = []
41
+ tools: list[Union[PlanTool, str]] = []
28
42
 
29
43
 
30
44
  class PlanDbContract(BaseModel):
@@ -37,11 +51,29 @@ class PlanPublish(BaseModel):
37
51
  model: Optional[str] = None
38
52
  payload: dict = {}
39
53
 
54
+ @model_validator(mode="before")
55
+ @classmethod
56
+ def _coerce_str(cls, data):
57
+ if isinstance(data, str):
58
+ return {
59
+ "event": data,
60
+ "model": payload_model_name(data),
61
+ "payload": {},
62
+ }
63
+ return data
64
+
40
65
 
41
66
  class PlanConsume(BaseModel):
42
67
  event: str
43
68
  requires: list[str] = []
44
69
 
70
+ @model_validator(mode="before")
71
+ @classmethod
72
+ def _coerce_str(cls, data):
73
+ if isinstance(data, str):
74
+ return {"event": data, "requires": []}
75
+ return data
76
+
45
77
 
46
78
  class PlanFeature(BaseModel):
47
79
  plugin: str
@@ -64,6 +96,16 @@ class PlanFeature(BaseModel):
64
96
 
65
97
  model_config = ConfigDict(populate_by_name=True)
66
98
 
99
+ @field_validator("publishes", "consumes", mode="before")
100
+ @classmethod
101
+ def _ensure_list(cls, data):
102
+ if data is None:
103
+ return []
104
+ if isinstance(data, (str, dict)):
105
+ return [data]
106
+ return data
107
+
108
+
67
109
 
68
110
  class PlanLink(BaseModel):
69
111
  consumes: str
@@ -0,0 +1,140 @@
1
+ """Checklist generator & synchronizer for MicroCoreOS execution plans.
2
+
3
+ Reads a Plan object (from `plans/active_plan.yaml`) and generates or updates
4
+ the markdown execution checklist at `plans/active_plan.md`.
5
+
6
+ Preserves existing `[x]` / `[X]` progress so synchronizing a plan does not
7
+ reset completed tasks, while ensuring 100% path coverage for Rule 15 and
8
+ eliminating the template marker so Rule 0 passes.
9
+ """
10
+ import os
11
+ from typing import Optional
12
+
13
+ from microcoreos_dev.plan.schema import Plan, PlanTool
14
+
15
+
16
+ def generate_checklist(plan: Plan, existing_checklist: Optional[str] = None) -> str:
17
+ """Generate or update the markdown checklist from a Plan instance.
18
+
19
+ Every file:, test:, migration, model and tool path is generated with a checkbox.
20
+ If existing_checklist contains checked boxes (`- [x]` or `- [X]`) for a path,
21
+ that checkmark is preserved in the updated output.
22
+ """
23
+ done_paths = set()
24
+ if existing_checklist:
25
+ for line in existing_checklist.splitlines():
26
+ stripped = line.strip()
27
+ if stripped.startswith(("- [x]", "- [X]", "* [x]", "* [X]")):
28
+ for token in line.split():
29
+ clean = token.strip("`(),*[]\"'")
30
+ if "/" in clean or clean.endswith((".py", ".sql")) or clean.startswith("microcoreos"):
31
+ done_paths.add(clean)
32
+ done_paths.add(os.path.basename(clean))
33
+
34
+ def mark(path: str) -> str:
35
+ if path in done_paths or os.path.basename(path) in done_paths:
36
+ return "- [x]"
37
+ return "- [ ]"
38
+
39
+ lines = [
40
+ "# Active Integration Plan — Execution Checklist",
41
+ "",
42
+ "> This file is the checklist / state machine for orchestrating subagents.",
43
+ "> The formal contract (routes, columns, events, flows) lives in `plans/active_plan.yaml`.",
44
+ "> The coordinator agent updates the task status as features are verified.",
45
+ "",
46
+ ]
47
+
48
+ # Phase 0: Foundation
49
+ p0 = plan.phase_0
50
+ has_p0 = bool(p0.tools or p0.migrations or p0.models)
51
+ if has_p0:
52
+ lines.append("## 🛠️ Phase 0: Foundation (Serial — built 1:1 from the plan's `phase_0`)")
53
+ for tool in p0.tools:
54
+ name = tool.name if isinstance(tool, PlanTool) else str(tool)
55
+ file_path = tool.file if isinstance(tool, PlanTool) else str(tool)
56
+ lines.append(f"{mark(file_path)} Task T_{name}: Tool `{name}` (`{file_path}`)")
57
+ for migration in p0.migrations:
58
+ lines.append(f"{mark(migration.file)} Task M_{os.path.basename(migration.file)}: SQL Migration (`{migration.file}`)")
59
+ if migration.tables:
60
+ lines.append(f" * **Tables owned**: `{migration.tables}`")
61
+ for model in p0.models:
62
+ lines.append(f"{mark(model)} Task D_{os.path.basename(model)}: Model (`{model}`)")
63
+ migrate_cmd = "microcoreos migrate"
64
+ lines.append(f"{mark(migrate_cmd)} Task P0_Migrate: Apply migrations (`{migrate_cmd}`)")
65
+ lines.append("")
66
+
67
+ # Phase 2: Plugins & Features
68
+ if plan.features:
69
+ lines.append("## 💻 Phase 2: Plugins & Features (Parallel)")
70
+ for idx, feat in enumerate(plan.features, 1):
71
+ lines.append(f"{mark(feat.file)} Task P{idx}: Feature Plugin `{feat.plugin}` (`{feat.file}`)")
72
+ details = []
73
+ if feat.route:
74
+ details.append(f"**Route**: `{feat.route.method.upper()} {feat.route.path}`")
75
+ if feat.publishes:
76
+ events = [p.event for p in feat.publishes]
77
+ details.append(f"**Publishes**: `{events}`")
78
+ if feat.consumes:
79
+ events = [c.event for c in feat.consumes]
80
+ details.append(f"**Consumes**: `{events}`")
81
+ if feat.tools:
82
+ details.append(f"**Tools**: `{feat.tools}`")
83
+ for d in details:
84
+ lines.append(f" * {d}")
85
+ if feat.test:
86
+ lines.append(f"{mark(feat.test)} Task P{idx}_Test: Unit Test (`{feat.test}`)")
87
+ lines.append("")
88
+
89
+ # Phase 3: Integration & Flow Verification
90
+ if plan.flows:
91
+ lines.append("## 🚦 Phase 3: Integration & Flow Verification (End-to-End)")
92
+ for idx, flow in enumerate(plan.flows, 1):
93
+ if flow.e2e_test:
94
+ lines.append(f"{mark(flow.e2e_test)} Task F{idx}_E2E: Flow `{flow.name}` E2E test (`{flow.e2e_test}`)")
95
+ if flow.sad_path_test:
96
+ lines.append(f"{mark(flow.sad_path_test)} Task F{idx}_Sad: Flow `{flow.name}` Sad-path test (`{flow.sad_path_test}`)")
97
+ for link in flow.links:
98
+ if link.idempotency_test:
99
+ lines.append(f"{mark(link.idempotency_test)} Task F{idx}_Idempotent: `{link.consumes} → {link.consumer}` (`{link.idempotency_test}`)")
100
+ lines.append("")
101
+
102
+ return "\n".join(lines).rstrip() + "\n"
103
+
104
+
105
+ def sync_checklist(plan_path: str = os.path.join("plans", "active_plan.yaml"),
106
+ checklist_path: str = os.path.join("plans", "active_plan.md")) -> tuple[bool, str]:
107
+ """Read plan_path, update checklist_path, and return (success, message)."""
108
+ if not os.path.isfile(plan_path):
109
+ return False, f"No plan file found at {plan_path}"
110
+
111
+ from microcoreos_dev.plan.rules import parse_plan_yaml
112
+
113
+ with open(plan_path, "r", encoding="utf-8") as f:
114
+ content = f.read()
115
+
116
+ plan_dict, err = parse_plan_yaml(content)
117
+ if err:
118
+ return False, f"Could not parse plan: {err}"
119
+
120
+ try:
121
+ plan = Plan(**plan_dict)
122
+ except Exception as e:
123
+ return False, f"Plan schema error: {e}"
124
+
125
+ existing = None
126
+ if os.path.isfile(checklist_path):
127
+ with open(checklist_path, "r", encoding="utf-8") as f:
128
+ existing = f.read()
129
+
130
+ generated = generate_checklist(plan, existing)
131
+
132
+ # Ensure parent dir exists
133
+ parent = os.path.dirname(checklist_path)
134
+ if parent:
135
+ os.makedirs(parent, exist_ok=True)
136
+
137
+ with open(checklist_path, "w", encoding="utf-8") as f:
138
+ f.write(generated)
139
+
140
+ return True, f"Synchronized {checklist_path} from {plan_path}"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "microcoreos"
3
- version = "0.4.2"
3
+ version = "0.4.4"
4
4
  description = "Atomic Microkernel Architecture optimized for AI-Driven Development"
5
5
  license = {text = "MIT"}
6
6
  readme = "README.md"
@@ -676,3 +676,49 @@ def test_probe_accepts_the_route_and_events_the_plan_declares(tmp_path, monkeypa
676
676
  monkeypatch.chdir(_route_project(tmp_path))
677
677
 
678
678
  assert cli.main(["plan", "probe"]) == 0
679
+
680
+
681
+ def test_plan_sync_cli_generates_active_plan_md(tmp_path, monkeypatch, capsys):
682
+ project = _probe_project(tmp_path)
683
+ monkeypatch.chdir(project)
684
+
685
+ checklist_file = project / "plans" / "active_plan.md"
686
+ assert not checklist_file.exists()
687
+
688
+ code = cli.main(["plan", "sync"])
689
+ assert code == 0
690
+ assert checklist_file.exists()
691
+
692
+ content = checklist_file.read_text(encoding="utf-8")
693
+ assert "CounterPlugin" in content
694
+ assert "domains/shop/plugins/counter_plugin.py" in content
695
+ assert "tests/test_counter_plugin.py" in content
696
+ assert "<!-- template: true -->" not in content
697
+
698
+
699
+ def test_plan_validate_fix_cli_syncs_checklist(tmp_path, monkeypatch, capsys):
700
+ valid_plan_yaml = """
701
+ plan:
702
+ domain: shop
703
+ features:
704
+ - plugin: CounterPlugin
705
+ file: domains/shop/plugins/counter_plugin.py
706
+ publishes: ["order.counted"]
707
+ tools: [event_bus]
708
+ test: tests/test_counter_plugin.py
709
+ """
710
+ project = _probe_project(tmp_path, plan=valid_plan_yaml)
711
+ monkeypatch.chdir(project)
712
+
713
+ # Place a template checklist
714
+ checklist_file = project / "plans" / "active_plan.md"
715
+ checklist_file.write_text("<!-- template: true -->\n- [ ] dummy", encoding="utf-8")
716
+
717
+ code = cli.main(["plan", "validate", "--fix"])
718
+ # Validation will pass because --fix updated the checklist with the real tasks
719
+ assert code == 0
720
+ content = checklist_file.read_text(encoding="utf-8")
721
+ assert "<!-- template: true -->" not in content
722
+ assert "counter_plugin.py" in content
723
+
724
+