microcoreos 0.3.0__tar.gz → 0.3.2__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.
- {microcoreos-0.3.0 → microcoreos-0.3.2}/.agent/skills/microcoreos-architecture/SKILL.md +5 -5
- {microcoreos-0.3.0 → microcoreos-0.3.2}/.agent/skills/microcoreos-architecture/agent.md +1 -1
- {microcoreos-0.3.0 → microcoreos-0.3.2}/.agent/workflows/feature-plan.md +14 -5
- {microcoreos-0.3.0 → microcoreos-0.3.2}/.agent/workflows/multi-domain-plan.md +5 -5
- {microcoreos-0.3.0 → microcoreos-0.3.2}/.agent/workflows/new-domain.md +70 -32
- {microcoreos-0.3.0 → microcoreos-0.3.2}/.agent/workflows/new-tool.md +21 -8
- {microcoreos-0.3.0 → microcoreos-0.3.2}/.claude/settings.local.json +2 -1
- {microcoreos-0.3.0 → microcoreos-0.3.2}/.github/workflows/ci.yml +8 -4
- {microcoreos-0.3.0 → microcoreos-0.3.2}/.gitignore +7 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/AGENTS.md +34 -4
- {microcoreos-0.3.0 → microcoreos-0.3.2}/AI_CONTEXT.md +17 -8
- {microcoreos-0.3.0 → microcoreos-0.3.2}/INSTRUCTIONS_FOR_AI.md +15 -11
- {microcoreos-0.3.0 → microcoreos-0.3.2}/PKG-INFO +21 -4
- {microcoreos-0.3.0 → microcoreos-0.3.2}/README.md +20 -3
- {microcoreos-0.3.0 → microcoreos-0.3.2}/ROADMAP.md +11 -11
- {microcoreos-0.3.0/tests → microcoreos-0.3.2}/conftest.py +8 -3
- {microcoreos-0.3.0 → microcoreos-0.3.2}/docs/CORE_INFRASTRUCTURE.md +1 -1
- {microcoreos-0.3.0 → microcoreos-0.3.2}/docs/ELASTIC_DEPLOYMENT.md +1 -1
- {microcoreos-0.3.0 → microcoreos-0.3.2}/docs/EVENT_BUS.md +1 -1
- {microcoreos-0.3.0 → microcoreos-0.3.2}/docs/INDEX.md +1 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/docs/OBSERVABILITY.md +4 -2
- {microcoreos-0.3.0 → microcoreos-0.3.2}/docs/PARALLEL_DEVELOPMENT.md +18 -2
- {microcoreos-0.3.0 → microcoreos-0.3.2}/domains/devtools/plugins/discovery_naming_linter_plugin.py +29 -0
- microcoreos-0.3.2/domains/devtools/plugins/doc_path_linter_plugin.py +193 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/domains/devtools/plugins/event_contract_linter_plugin.py +2 -0
- microcoreos-0.3.0/tests/test_auth_tool.py → microcoreos-0.3.2/extras/available_tools/auth/tests/auth_tool_test.py +16 -22
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_tools/postgresql/postgresql_tool.py +1 -1
- microcoreos-0.3.0/tests/test_postgresql_describe_schema.py → microcoreos-0.3.2/extras/available_tools/postgresql/tests/postgresql_describe_schema_test.py +36 -28
- microcoreos-0.3.0/tests/test_postgresql_tool.py → microcoreos-0.3.2/extras/available_tools/postgresql/tests/postgresql_tool_test.py +15 -11
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_tools/rabbitmq/rabbitmq_driver.py +7 -6
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_tools/s3/s3_tool.py +28 -13
- microcoreos-0.3.0/tests/test_s3_tool.py → microcoreos-0.3.2/extras/available_tools/s3/tests/s3_tool_test.py +67 -8
- microcoreos-0.3.0/tests/test_scheduler_singleton.py → microcoreos-0.3.2/extras/available_tools/scheduler/tests/scheduler_singleton_test.py +13 -8
- microcoreos-0.3.0/tests/test_scheduler_tool.py → microcoreos-0.3.2/extras/available_tools/scheduler/tests/scheduler_tool_test.py +12 -23
- {microcoreos-0.3.0 → microcoreos-0.3.2}/microcoreos/cli.py +1 -1
- {microcoreos-0.3.0 → microcoreos-0.3.2}/microcoreos/scaffold.py +126 -37
- {microcoreos-0.3.0 → microcoreos-0.3.2}/microcoreos_dev/__init__.py +2 -2
- {microcoreos-0.3.0 → microcoreos-0.3.2}/microcoreos_dev/cli.py +1 -1
- {microcoreos-0.3.0 → microcoreos-0.3.2}/microcoreos_dev/fuzzer.py +1 -1
- {microcoreos-0.3.0 → microcoreos-0.3.2}/microcoreos_dev/plan/rules.py +1 -1
- {microcoreos-0.3.0 → microcoreos-0.3.2}/pyproject.toml +14 -2
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/core}/test_core.py +137 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/core}/test_core_purity.py +4 -4
- microcoreos-0.3.2/tests/core/test_kernel.py +348 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/core}/test_plugin_di_fixtures.py +1 -1
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/core}/test_tool_proxy.py +12 -1
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tests/dev/test_plan_validator.py +3 -3
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tests/helpers/active_db.py +1 -1
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/linters}/test_discovery_naming_linter.py +29 -0
- microcoreos-0.3.2/tests/linters/test_doc_path_linter.py +128 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/system}/test_catalog.py +1 -1
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/system}/test_cli.py +1 -1
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/system}/test_scaffold.py +17 -9
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/system}/test_upgrade.py +4 -3
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/tools/context}/test_context_tool.py +78 -3
- {microcoreos-0.3.0/tests/tools → microcoreos-0.3.2/tests/tools/event_bus}/test_event_bus_broker_parity.py +1 -1
- {microcoreos-0.3.0/tests/tools → microcoreos-0.3.2/tests/tools/event_bus}/test_event_bus_kafka_parity.py +1 -1
- {microcoreos-0.3.0/tests/tools → microcoreos-0.3.2/tests/tools/event_bus}/test_event_bus_rabbitmq_parity.py +1 -1
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/tools/event_bus}/test_event_bus_tool.py +60 -0
- microcoreos-0.3.2/tests/tools/http_server/test_http_pipeline.py +222 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/tools/http_server}/test_http_server_tool.py +56 -3
- microcoreos-0.3.2/tests/tools/registry/test_registry_tool.py +52 -0
- {microcoreos-0.3.0/tests/tools → microcoreos-0.3.2/tests/tools/state}/test_state_parity.py +1 -1
- microcoreos-0.3.2/tests/tools/telemetry/test_telemetry_tool.py +390 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tools/event_bus/event_bus_tool.py +1 -1
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tools/event_bus/sqlite_driver.py +1 -1
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tools/http_server/http_server_tool.py +1 -1
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tools/sqlite/sqlite_tool.py +27 -12
- microcoreos-0.3.2/tools/telemetry/__init__.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tools/telemetry/telemetry_tool.py +102 -8
- {microcoreos-0.3.0 → microcoreos-0.3.2}/uv.lock +424 -1
- microcoreos-0.3.0/tests/test_kernel.py +0 -131
- {microcoreos-0.3.0 → microcoreos-0.3.2}/.dockerignore +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/.env.example +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/.github/workflows/release.yml +0 -0
- /microcoreos-0.3.0/extras/available_tools/s3/__init__.py → /microcoreos-0.3.2/.mutmut-cache +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/.python-version +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/Dockerfile +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/LICENSE +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/cli.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/dev_infra/cache_probe.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/dev_infra/docker-compose.yml +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/docs/CLI.md +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/docs/HTTP_SERVER.md +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/docs/OBSERVABILITY_API.md +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/docs/translations/es/README.md +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/domains/devtools/lint/plugin_sources.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/domains/devtools/plugins/domain_isolation_linter_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/domains/devtools/plugins/event_schemas_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/domains/devtools/plugins/field_divergence_linter_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/domains/devtools/plugins/route_collision_linter_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/domains/devtools/plugins/table_ownership_linter_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/domains/devtools/plugins/tool_doc_drift_linter_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/domains/system/plugins/event_delivery_monitor_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/domains/system/plugins/system_events_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/domains/system/plugins/system_events_stream_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/domains/system/plugins/system_logs_stream_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/domains/system/plugins/system_metrics_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/domains/system/plugins/system_status_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/domains/system/plugins/system_traces_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/domains/system/plugins/system_traces_stream_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/domains/system/plugins/tool_health_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_domains/chaos/plugins/blocking_boot_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_domains/chaos/plugins/chaos_control_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_domains/chaos/plugins/failing_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_domains/chaos/plugins/stress_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_domains/ping/plugins/ping_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_domains/scheduler/migrations/001_scheduler_one_shots.sql +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_domains/scheduler/models/scheduler_one_shot.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_domains/scheduler/plugins/durable_one_shots_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_domains/users/migrations/001_create_users.sql +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_domains/users/models/user.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_domains/users/plugins/create_user_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_domains/users/plugins/delete_user_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_domains/users/plugins/get_me_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_domains/users/plugins/get_user_by_id_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_domains/users/plugins/get_users_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_domains/users/plugins/login_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_domains/users/plugins/logout_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_domains/users/plugins/update_user_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_domains/users/plugins/welcome_service_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_tools/auth/auth_tool.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_tools/chaos/chaos_tool.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_tools/kafka/kafka_driver.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_tools/redis_state/redis_state_tool.py +0 -0
- {microcoreos-0.3.0/tools/telemetry → microcoreos-0.3.2/extras/available_tools/s3}/__init__.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/extras/available_tools/scheduler/scheduler_tool.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/hatch_build.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/main.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/microcoreos/__init__.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/microcoreos/base_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/microcoreos/base_tool.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/microcoreos/catalog.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/microcoreos/container.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/microcoreos/context.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/microcoreos/kernel.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/microcoreos/project.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/microcoreos/project_readme.md +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/microcoreos/registry.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/microcoreos/upgrade.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/microcoreos_dev/pipeline.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/microcoreos_dev/plan/__init__.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/microcoreos_dev/plan/scan.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/microcoreos_dev/plan/schema.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/microcoreos_dev/probe.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/plans/README.md +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/plans/active_plan.md +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/plans/active_plan.yaml +0 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/core}/test_no_retry.py +0 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/core}/test_registry_collisions.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tests/dev/corpus/README.md +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tests/dev/corpus/qwen_twitter_plan.yaml +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tests/dev/test_pipeline.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tests/domains/ping/test_ping_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tests/domains/users/test_create_user_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tests/domains/users/test_delete_user_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tests/domains/users/test_get_me_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tests/domains/users/test_get_user_by_id_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tests/domains/users/test_get_users_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tests/domains/users/test_login_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tests/domains/users/test_logout_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tests/domains/users/test_update_user_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tests/domains/users/test_welcome_service_plugin.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tests/helpers/async_wait.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tests/helpers/mock_db.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tests/helpers/trace_chains.py +0 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/linters}/test_domain_isolation_linter.py +0 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/linters}/test_event_contract_linter.py +0 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/linters}/test_field_divergence_linter.py +0 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/linters}/test_route_collision_linter.py +0 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/linters}/test_table_ownership_linter.py +0 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/linters}/test_tool_doc_drift_linter.py +0 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/system}/test_chaos_control.py +0 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/system}/test_chaos_tool.py +0 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/system}/test_durable_one_shots.py +0 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/system}/test_event_schemas_plugin.py +0 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/system}/test_system_events_stats.py +0 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/system}/test_system_traces_plugin.py +0 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/system}/test_trace_chain_helper.py +0 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/tools/config}/test_config_tool.py +0 -0
- {microcoreos-0.3.0/tests/tools → microcoreos-0.3.2/tests/tools/db}/test_db_parity.py +0 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/tools/event_bus}/test_event_bus_groups.py +0 -0
- {microcoreos-0.3.0/tests/tools → microcoreos-0.3.2/tests/tools/event_bus}/test_redis_streams_driver.py +0 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/tools/http_server}/test_http_params_hardening.py +0 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/tools/http_server}/test_security_hardening.py +0 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/tools/logger}/test_logger_tool.py +0 -0
- {microcoreos-0.3.0/tests/tools → microcoreos-0.3.2/tests/tools/s3}/test_s3_parity.py +0 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/tools/sqlite}/test_sqlite_concurrency.py +0 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/tools/sqlite}/test_sqlite_describe_schema.py +0 -0
- {microcoreos-0.3.0/tests/tools → microcoreos-0.3.2/tests/tools/sqlite}/test_sqlite_driver.py +0 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/tools/sqlite}/test_sqlite_migrations.py +0 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/tools/sqlite}/test_sqlite_tool.py +0 -0
- {microcoreos-0.3.0/tests → microcoreos-0.3.2/tests/tools/state}/test_state_tool.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tools/config/config_tool.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tools/context/authoring_guide.md +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tools/context/context_tool.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tools/context/renderers.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tools/context/scanners.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tools/event_bus/drivers.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tools/event_bus/envelope.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tools/event_bus/redis_streams_driver.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tools/http_server/context.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tools/http_server/pipeline.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tools/http_server/types.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tools/logger/logger_tool.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tools/sqlite/errors.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tools/sqlite/migrations.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tools/sqlite/transaction.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tools/state/state_tool.py +0 -0
- {microcoreos-0.3.0 → microcoreos-0.3.2}/tools/system/registry_tool.py +0 -0
|
@@ -11,14 +11,14 @@ open — a Planner that also loads the plugin templates spends ~3,000 tokens on
|
|
|
11
11
|
code it will never write.
|
|
12
12
|
|
|
13
13
|
This file deliberately holds no rules, no reading path and no checklist of its
|
|
14
|
-
own. It
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
own. It is a routing table and nothing else: every row points at the one place
|
|
15
|
+
that owns its subject. Add a rule, a path or a checklist here and it becomes a
|
|
16
|
+
rival copy of something canonical — which is the one failure this file exists
|
|
17
|
+
to prevent.
|
|
18
18
|
|
|
19
19
|
| You need | It is in |
|
|
20
20
|
|---|---|
|
|
21
|
-
| The rules | `AGENTS.md` § Non-Negotiable Rules (
|
|
21
|
+
| The rules | `AGENTS.md` § Non-Negotiable Rules (canonical) |
|
|
22
22
|
| Kernel/tool/event-bus laws | `AGENTS.md` § Core Architectural Laws |
|
|
23
23
|
| The plugin template | `AI_CONTEXT.md` § Plugin Authoring Guide (regenerated every boot) |
|
|
24
24
|
| What exists right now | `AI_CONTEXT.md` § Available Tools / § Domains |
|
|
@@ -10,7 +10,7 @@ You are a **Systems Architect** specialized in high-performance, resilient micro
|
|
|
10
10
|
## Decision Framework
|
|
11
11
|
- **Core First**: Is this change affecting the Core? If yes, look for an alternative in Plugins/Tools.
|
|
12
12
|
- **Tool Isolation**: Does this Tool import another Tool? If so, REJECT the design and move the logic to a Plugin (Bridge).
|
|
13
|
-
- **Rules Review**: Before implementing, check the
|
|
13
|
+
- **Rules Review**: Before implementing, check the Non-Negotiable Rules in `AGENTS.md`.
|
|
14
14
|
- **Resilience**: Will a failure here crash the entire system?
|
|
15
15
|
- **Observability**: Can this be monitored via the `registry`?
|
|
16
16
|
|
|
@@ -30,8 +30,8 @@ existing vocabulary implies, put it in the YAML, and flag it in a comment.
|
|
|
30
30
|
|
|
31
31
|
`docs/PARALLEL_DEVELOPMENT.md` § Phase 1 holds the rules behind the format.
|
|
32
32
|
Read it when a validator error is unclear, not before — and never reach for
|
|
33
|
-
plugin source under `domains/`, `tools/` or `extras/` to infer the shape
|
|
34
|
-
|
|
33
|
+
plugin source under `domains/`, `tools/` or `extras/` to infer the shape:
|
|
34
|
+
reading an implementation to infer the format renames every field in your plan.
|
|
35
35
|
|
|
36
36
|
## Prerequisites
|
|
37
37
|
|
|
@@ -127,10 +127,19 @@ assert_chain(build_tree(bus.get_trace_history()), ["order.cancelled", "order.ref
|
|
|
127
127
|
### 5. Close
|
|
128
128
|
|
|
129
129
|
```bash
|
|
130
|
-
|
|
131
|
-
uv run main.py # or: microcoreos (if you installed the package)
|
|
130
|
+
microcoreos migrate # the boot that ends: applies migrations, regenerates AI_CONTEXT.md
|
|
132
131
|
```
|
|
133
132
|
|
|
134
|
-
- `GET /system/lint` → no warnings, no `UNTYPED_PAYLOAD` for your events.
|
|
135
133
|
- Regenerated `AI_CONTEXT.md` matches the plan (routes, events, keys).
|
|
136
134
|
**The feature is done when AI_CONTEXT == plan.**
|
|
135
|
+
|
|
136
|
+
Then, for the lint only — the one boot this workflow sanctions (`AGENTS.md`
|
|
137
|
+
§ reading route). This boot **serves forever**: foreground, read, Ctrl-C.
|
|
138
|
+
Never background it; a process holding port 5000 makes the next
|
|
139
|
+
`microcoreos migrate` refuse to run.
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
microcoreos # or: uv run main.py (identical) — Ctrl-C when done
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
- `GET /system/lint` → no warnings, no `UNTYPED_PAYLOAD` for your events.
|
|
@@ -35,8 +35,8 @@ existing vocabulary implies, put it in the YAML, and flag it in a comment.
|
|
|
35
35
|
|
|
36
36
|
`docs/PARALLEL_DEVELOPMENT.md` § Phase 1 holds the rules behind the format.
|
|
37
37
|
Read it when a validator error is unclear, not before — and never reach for
|
|
38
|
-
plugin source under `domains/`, `tools/` or `extras/` to infer the shape
|
|
39
|
-
|
|
38
|
+
plugin source under `domains/`, `tools/` or `extras/` to infer the shape:
|
|
39
|
+
reading an implementation to infer the format renames every field in your plan.
|
|
40
40
|
|
|
41
41
|
## Phase 1 — The full plan (the contract, authored FIRST)
|
|
42
42
|
|
|
@@ -73,14 +73,14 @@ never patch it in code.
|
|
|
73
73
|
## Phases 0, 2 and 3
|
|
74
74
|
|
|
75
75
|
Identical to any other plan — `docs/PARALLEL_DEVELOPMENT.md` owns them, and
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
nothing about them is restated here. A second copy of a phase is a second
|
|
77
|
+
command to keep in step, and only one of the two gets updated.
|
|
78
78
|
|
|
79
79
|
Two things are specific to multi-domain work and are the only reason this
|
|
80
80
|
section exists:
|
|
81
81
|
|
|
82
82
|
- **Migration ordering across domains.** One author for the numbering, and
|
|
83
|
-
`-- depends: other_domain/001_file.sql` wherever a table in one domain must
|
|
83
|
+
`-- depends: other_domain/001_file.sql` wherever a table in one domain must <!-- lint:no-path -->
|
|
84
84
|
exist before another's. The db tool resolves the order and prints each file
|
|
85
85
|
as it applies it.
|
|
86
86
|
- **Never assign two agents to the same feature**, and never let one agent
|
|
@@ -32,13 +32,13 @@ existing vocabulary implies, put it in the YAML, and flag it in a comment.
|
|
|
32
32
|
|
|
33
33
|
`docs/PARALLEL_DEVELOPMENT.md` § Phase 1 holds the rules behind the format.
|
|
34
34
|
Read it when a validator error is unclear, not before — and never reach for
|
|
35
|
-
plugin source under `domains/`, `tools/` or `extras/` to infer the shape
|
|
36
|
-
|
|
35
|
+
plugin source under `domains/`, `tools/` or `extras/` to infer the shape:
|
|
36
|
+
reading an implementation to infer the format renames every field in your plan.
|
|
37
37
|
|
|
38
38
|
## Prerequisites
|
|
39
39
|
|
|
40
40
|
Both files above. Nothing else: the plugin template lives in `AI_CONTEXT.md`
|
|
41
|
-
§ Plugin Authoring Guide, generated on every boot, and the rules are the
|
|
41
|
+
§ Plugin Authoring Guide, generated on every boot, and the rules are the
|
|
42
42
|
Non-Negotiable Rules in `AGENTS.md`.
|
|
43
43
|
|
|
44
44
|
## Steps
|
|
@@ -65,7 +65,6 @@ lines of YAML, one pass.
|
|
|
65
65
|
### 1. Create the domain folder structure
|
|
66
66
|
|
|
67
67
|
```bash
|
|
68
|
-
// turbo
|
|
69
68
|
mkdir -p domains/{name}/models domains/{name}/migrations domains/{name}/plugins
|
|
70
69
|
```
|
|
71
70
|
|
|
@@ -93,13 +92,24 @@ class {Name}Entity(BaseModel):
|
|
|
93
92
|
|
|
94
93
|
File: `domains/{name}/migrations/001_create_{name}_table.sql`
|
|
95
94
|
|
|
96
|
-
Write raw SQL that creates the table.
|
|
95
|
+
Write raw SQL that creates the table. Queries use `$1, $2...` placeholders
|
|
96
|
+
(PostgreSQL-style, auto-converted for SQLite) — but **migration SQL is never
|
|
97
|
+
translated: it runs verbatim on the active engine** (Non-Negotiable Rule 8).
|
|
98
|
+
The default engine is SQLite, which has neither `SERIAL` nor `NOW()`.
|
|
99
|
+
|
|
100
|
+
Write portable SQL — `CURRENT_TIMESTAMP`, not `NOW()` — so the PostgreSQL swap
|
|
101
|
+
stays free. The auto-increment PK is the one thing with no spelling common to
|
|
102
|
+
both: write it the SQLite way and carry the swap note in the file. The
|
|
103
|
+
reference is `extras/available_domains/users/migrations/001_create_users.sql`:
|
|
97
104
|
|
|
98
105
|
```sql
|
|
106
|
+
-- Portable SQL except the auto-increment PK. On a PostgreSQL swap, change
|
|
107
|
+
-- "INTEGER PRIMARY KEY" to "INTEGER PRIMARY KEY GENERATED BY DEFAULT AS
|
|
108
|
+
-- IDENTITY" (see docs/ELASTIC_DEPLOYMENT.md, Stage 1 review).
|
|
99
109
|
CREATE TABLE IF NOT EXISTS {name}s (
|
|
100
|
-
id
|
|
101
|
-
-- columns matching the entity model
|
|
102
|
-
created_at TIMESTAMP DEFAULT
|
|
110
|
+
id INTEGER PRIMARY KEY,
|
|
111
|
+
-- columns matching the entity model, with the plan's types
|
|
112
|
+
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
103
113
|
);
|
|
104
114
|
```
|
|
105
115
|
|
|
@@ -108,7 +118,8 @@ CREATE TABLE IF NOT EXISTS {name}s (
|
|
|
108
118
|
One plugin file per operation in `domains/{name}/plugins/`. The template — with
|
|
109
119
|
request, response and event-payload schemas inline, and the rules that go with
|
|
110
120
|
them — is `AI_CONTEXT.md` § **Plugin Authoring Guide**, regenerated on every
|
|
111
|
-
boot and already inside every executor prompt.
|
|
121
|
+
boot and already inside every executor prompt. Never copy it into this file
|
|
122
|
+
or into a plan — the copy is what goes stale.
|
|
112
123
|
|
|
113
124
|
The one thing that is this workflow's own decision: which operations exist.
|
|
114
125
|
A CRUD domain is create / get_all / get_by_id / update / delete, one file each,
|
|
@@ -116,54 +127,81 @@ each declared in the plan before any of them is written.
|
|
|
116
127
|
|
|
117
128
|
### 5. Verify
|
|
118
129
|
|
|
130
|
+
Most of the checklist needs no server. Start with the boot that ends:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
microcoreos migrate # applies migrations AND regenerates AI_CONTEXT.md
|
|
134
|
+
```
|
|
135
|
+
|
|
119
136
|
```bash
|
|
120
|
-
|
|
121
|
-
uv run main.py # or: microcoreos (if you installed the package)
|
|
137
|
+
microcoreos schema # the live tables and columns
|
|
122
138
|
```
|
|
123
139
|
|
|
124
140
|
Check that:
|
|
125
|
-
-
|
|
141
|
+
- Migrations applied (look for `[Migration] ✅`) and `microcoreos schema` shows
|
|
142
|
+
the new tables with the columns the plan declared
|
|
143
|
+
- `AI_CONTEXT.md` was regenerated with the new domain — **done when it matches
|
|
144
|
+
the plan**
|
|
145
|
+
|
|
146
|
+
Only the lint endpoints need a live system, and this is the one place in this
|
|
147
|
+
workflow sanctioned to boot one (`AGENTS.md` § reading route). This boot
|
|
148
|
+
**serves forever**: run it in the foreground, read the endpoints, Ctrl-C.
|
|
149
|
+
Never background it — a process left holding port 5000 makes the next
|
|
150
|
+
`microcoreos migrate` refuse to run.
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
microcoreos # or: uv run main.py (identical) — Ctrl-C when done
|
|
154
|
+
```
|
|
155
|
+
|
|
126
156
|
- Endpoints appear in the Swagger UI at `http://localhost:5000/docs`
|
|
127
157
|
- `GET /system/lint` has no warnings and no `UNTYPED_PAYLOAD` for your events
|
|
128
158
|
- `GET /system/events/schemas` lists every event the plan declared
|
|
129
|
-
- `AI_CONTEXT.md` was regenerated with the new domain — **done when it matches the plan**
|
|
130
|
-
- `microcoreos schema` shows the new tables with the columns the plan declared
|
|
131
159
|
|
|
132
160
|
### 6. Generate tests
|
|
133
161
|
|
|
134
162
|
Create `tests/test_{name}_plugin.py` with one test per plugin. Mock exactly
|
|
135
163
|
the tools the plan's `tools:` field lists; run the rest as real in-memory
|
|
136
|
-
instances (`INSTRUCTIONS_FOR_AI.md` § Testing).
|
|
137
|
-
|
|
164
|
+
instances (`INSTRUCTIONS_FOR_AI.md` § Testing).
|
|
165
|
+
|
|
166
|
+
**Configure the method, not the mock.** A plugin awaits `self.db.execute(...)`,
|
|
167
|
+
never `self.db(...)` — so `AsyncMock(return_value=1)` and
|
|
168
|
+
`AsyncMock(side_effect=...)` configure a call that never happens. Set them on
|
|
169
|
+
the method: `db.execute.return_value = 1`, `db.execute.side_effect = ...`.
|
|
138
170
|
|
|
139
171
|
```python
|
|
140
172
|
import pytest
|
|
141
173
|
from unittest.mock import MagicMock, AsyncMock
|
|
142
174
|
from domains.{name}.plugins.create_{name}_plugin import Create{Name}Plugin
|
|
143
175
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
event_bus=AsyncMock(),
|
|
150
|
-
logger=MagicMock(),
|
|
176
|
+
pytestmark = pytest.mark.anyio
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def make_plugin(db):
|
|
180
|
+
return Create{Name}Plugin(
|
|
181
|
+
http=MagicMock(), db=db, event_bus=AsyncMock(), logger=MagicMock()
|
|
151
182
|
)
|
|
152
|
-
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
async def test_create_{name}_success():
|
|
186
|
+
db = AsyncMock()
|
|
187
|
+
db.execute.return_value = 1 # the METHOD the plugin awaits
|
|
188
|
+
result = await make_plugin(db).execute({"field1": "value", "field2": 42})
|
|
153
189
|
assert result["success"] is True
|
|
154
190
|
assert result["data"]["id"] == 1
|
|
155
191
|
|
|
156
|
-
|
|
192
|
+
|
|
157
193
|
async def test_create_{name}_db_error():
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
event_bus=AsyncMock(),
|
|
162
|
-
logger=MagicMock(),
|
|
163
|
-
)
|
|
164
|
-
result = await plugin.execute({"field1": "value", "field2": 42})
|
|
194
|
+
db = AsyncMock()
|
|
195
|
+
db.execute.side_effect = Exception("DB down")
|
|
196
|
+
result = await make_plugin(db).execute({"field1": "value", "field2": 42})
|
|
165
197
|
assert result["success"] is False
|
|
166
198
|
assert "DB down" not in result["error"] # Safe Errors: technical detail never reaches the client
|
|
167
199
|
```
|
|
168
200
|
|
|
201
|
+
`pytest.mark.anyio` needs an `anyio_backend` fixture; the generated
|
|
202
|
+
the generated root `conftest.py` already ships one, along with `mock_logger` and
|
|
203
|
+
`mock_state`. The richer alternative — the real db tool with this domain's
|
|
204
|
+
migrations applied, asserting on rows instead of on mock calls — is the
|
|
205
|
+
black-box style of `tests/domains/users/test_create_user_plugin.py`.
|
|
206
|
+
|
|
169
207
|
Run with `uv run -m pytest tests/test_{name}_plugin.py`.
|
|
@@ -20,16 +20,29 @@ else about them exists so they can be swapped without touching a single plugin.
|
|
|
20
20
|
1. **Location**: `tools/{name}/{name}_tool.py` — or `extras/available_tools/{name}/`
|
|
21
21
|
if it should not be active by default (a replacement ALWAYS starts in
|
|
22
22
|
extras/: two tools with the same `name` silently overwrite each other).
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
**The tool's own tests go in `{that folder}/tests/`**, so they travel with
|
|
24
|
+
the tool when `microcoreos add` moves it. Import the tool the way those
|
|
25
|
+
tests do — `tools.{name}` first, `extras.available_tools.{name}` as the
|
|
26
|
+
fallback — or the import dies the moment the folder moves. Parity suites
|
|
27
|
+
are the exception: they import two implementations at once, so they stay in
|
|
28
|
+
`tests/tools/{name}/`.
|
|
29
|
+
2. **Name those tests `{name}_tool_test.py`, never `test_{name}_tool.py`.**
|
|
30
|
+
The Kernel imports every `*_tool.py` under `tools/`, and `test_s3_tool.py`
|
|
31
|
+
ends in exactly that: boot would import the test, and with it pytest, which
|
|
32
|
+
a deployed install does not have. pytest collects `*_test.py` by default,
|
|
33
|
+
so the safe name costs nothing. `DiscoveryNamingLinterPlugin` fails on the
|
|
34
|
+
unsafe one — this is the only place in the repo where `*_test.py` is the
|
|
35
|
+
required form.
|
|
36
|
+
3. **The `name` property is the contract** — it is the DI injection key.
|
|
37
|
+
4. **A tool never uses other tools.** If a capability needs `db` + `event_bus`
|
|
25
38
|
+ `scheduler`, it is not a tool: compose it in the plugin layer
|
|
26
39
|
(precedents: DurableOneShotsPlugin, the deferred Outbox — Issue 28).
|
|
27
|
-
|
|
40
|
+
5. **Self-documented**: every public method appears in
|
|
28
41
|
`get_interface_description()` — the anti-drift linter warns on discrepancies.
|
|
29
|
-
|
|
30
|
-
|
|
42
|
+
6. **Config via `os.getenv()`** inside the tool (the `config` tool is for plugins).
|
|
43
|
+
7. **Header spec**: the tool's docstring/header documents its replacement
|
|
31
44
|
contract — the exact API and semantics a substitute must honor.
|
|
32
|
-
|
|
45
|
+
8. **External backend?** Make its connection-error class inherit
|
|
33
46
|
`ToolUnavailableError` so ToolProxy marks it DEAD on the first
|
|
34
47
|
infrastructure failure. In-memory/local tools skip this.
|
|
35
48
|
|
|
@@ -52,8 +65,8 @@ No imports from other tools, no plugin imports, stateless where possible.
|
|
|
52
65
|
|
|
53
66
|
The same test battery runs against the reference implementation AND yours:
|
|
54
67
|
|
|
55
|
-
- Canonical examples: `tests/tools/test_state_parity.py`,
|
|
56
|
-
`tests/tools/test_event_bus_broker_parity.py` (parameterized over transports).
|
|
68
|
+
- Canonical examples: `tests/tools/state/test_state_parity.py`,
|
|
69
|
+
`tests/tools/event_bus/test_event_bus_broker_parity.py` (parameterized over transports).
|
|
57
70
|
- If the backend needs a server, the suite skips itself when unavailable and
|
|
58
71
|
the server is added to CI services (`dev_infra/docker-compose.yml`).
|
|
59
72
|
- **A replacement that does not pass the reference's parity suite is not a
|
|
@@ -35,7 +35,7 @@ jobs:
|
|
|
35
35
|
--health-timeout 5s
|
|
36
36
|
--health-retries 5
|
|
37
37
|
|
|
38
|
-
# Real Redis so the state parity suite (tests/tools/test_state_parity.py)
|
|
38
|
+
# Real Redis so the state parity suite (tests/tools/state/test_state_parity.py)
|
|
39
39
|
# runs against actual infrastructure instead of skipping itself.
|
|
40
40
|
redis:
|
|
41
41
|
image: redis:7-alpine
|
|
@@ -48,7 +48,7 @@ jobs:
|
|
|
48
48
|
--health-retries 5
|
|
49
49
|
|
|
50
50
|
# Real RabbitMQ so the broker parity suite
|
|
51
|
-
# (tests/tools/test_event_bus_rabbitmq_parity.py) runs instead of skipping.
|
|
51
|
+
# (tests/tools/event_bus/test_event_bus_rabbitmq_parity.py) runs instead of skipping.
|
|
52
52
|
rabbitmq:
|
|
53
53
|
image: rabbitmq:3.13-alpine
|
|
54
54
|
ports:
|
|
@@ -103,7 +103,11 @@ jobs:
|
|
|
103
103
|
uv run python -c "from microcoreos import BaseTool; print('✅ BaseTool OK')"
|
|
104
104
|
|
|
105
105
|
- name: Run Core Tests
|
|
106
|
-
|
|
106
|
+
# No path argument: `testpaths` in pyproject.toml owns the set, and it
|
|
107
|
+
# is wider than tests/ — a tool's own tests live inside the tool's
|
|
108
|
+
# folder so they travel with it when `microcoreos add` moves it.
|
|
109
|
+
# Passing `tests/` here would silently drop every one of them.
|
|
110
|
+
run: uv run -m pytest -v --tb=short
|
|
107
111
|
|
|
108
112
|
# Boots the REAL system against REAL infrastructure (MinIO as S3, SQLite on
|
|
109
113
|
# disk) and asserts that every tool is OK and every plugin is READY. This is
|
|
@@ -373,7 +377,7 @@ jobs:
|
|
|
373
377
|
# The filesystem-facing suites. The rest of tests/ needs Postgres, Redis
|
|
374
378
|
# and RabbitMQ, which is what the Linux matrix is for.
|
|
375
379
|
- name: Path and packaging suites
|
|
376
|
-
run: uv run -m pytest tests/test_upgrade.py tests/test_scaffold.py tests/test_catalog.py tests/test_cli.py -q
|
|
380
|
+
run: uv run -m pytest tests/system/test_upgrade.py tests/system/test_scaffold.py tests/system/test_catalog.py tests/system/test_cli.py -q
|
|
377
381
|
|
|
378
382
|
- name: Build the wheel
|
|
379
383
|
run: uv build --wheel -o dist
|
|
@@ -27,12 +27,42 @@ Four roles do the work, and **each needs a different slice**. Reading outside
|
|
|
27
27
|
your slice is not thoroughness, it is budget: the Planner that also loads the
|
|
28
28
|
plugin templates spends ~3,000 tokens on code it will never write.
|
|
29
29
|
|
|
30
|
+
They are phases, not people. Doing all of them yourself is the fifth row — the
|
|
31
|
+
common case for one or two features — and it means playing the four in order,
|
|
32
|
+
taking each slice as you enter it, not reading everything up front.
|
|
33
|
+
|
|
30
34
|
| You are… | Read exactly this | **Write exactly this** | Not this |
|
|
31
35
|
|---|---|---|---|
|
|
32
36
|
| **Planner** — turning a request into a plan | **1.** `plans/active_plan.yaml` — the file you are about to overwrite. It ships as a worked example of all three feature shapes, and it is the cheapest, densest statement of the format there is. **2.** `AI_CONTEXT.md` **down to `## 🧩 Plugin Authoring Guide`** (existing tools, tables, models, routes, events). **3.** `docs/PARALLEL_DEVELOPMENT.md` **§ Phase 1** for the rules behind it | **`plans/active_plan.yaml` + `plans/active_plan.md`, overwriting them.** Never a new filename — `plans/twitter_plan.yaml` is a plan nothing will execute | The Authoring Guide, Phases 2-3, `domains/`, `tools/`, `tests/`, `extras/` |
|
|
33
37
|
| **Phase 0 Builder** — migrations, models, tools | `plans/active_plan.yaml` **§ phase_0** only | Exactly the files `phase_0` names: its migrations, its models, its tools | Everything else. The plan already decided every column |
|
|
34
38
|
| **Executor** — one plugin + its test | **Nothing.** Your prompt already contains `AI_CONTEXT.md` + the plan + your one task line | Exactly two files: the `file:` and `test:` your task declares | Any file at all — opening one only invites guessed paths |
|
|
35
39
|
| **Coordinator** — dispatch, verify, reconstruct | `plans/active_plan.md` (the checklist/state machine) + `docs/PARALLEL_DEVELOPMENT.md` **§ Phases 2-3** | Only the checkboxes in `plans/active_plan.md` | The plan's internals; the checklist is the state |
|
|
40
|
+
| **Solo** — one agent, every phase, in sequence | The row above you, as you reach it — the Planner's slice first, and nothing else until you are past planning | Everything the four rows write, in their order | Skipping the plan because it is "only two features". See below |
|
|
41
|
+
|
|
42
|
+
**If you are the Solo row**, exactly two things differ from the four above.
|
|
43
|
+
You may read the code **you** wrote — the black box rule is about not seeing
|
|
44
|
+
*another agent's* implementation, and it does not apply to your own
|
|
45
|
+
(`docs/PARALLEL_DEVELOPMENT.md` § Features are black boxes). And you run the
|
|
46
|
+
union of the command rows below, each in its own phase.
|
|
47
|
+
|
|
48
|
+
What does **not** differ is the plan. `plans/active_plan.yaml` is still the
|
|
49
|
+
first file you write, for one feature as much as for twelve. Three reasons,
|
|
50
|
+
none of which are about coordinating anybody:
|
|
51
|
+
|
|
52
|
+
- **The shipped plan is a valid plan.** Leave it untouched and `microcoreos
|
|
53
|
+
status` reports the *example* domain as the active work — to you now, and to
|
|
54
|
+
whoever opens the project next.
|
|
55
|
+
- **`microcoreos plan validate` is the only pre-code check there is.** Route
|
|
56
|
+
collisions, consumed events that nothing publishes, tables owned by another
|
|
57
|
+
domain, the sad-path checklist. It runs offline, in a second, before a line
|
|
58
|
+
of code commits you to any of it.
|
|
59
|
+
- **It is what survives you.** A context that gets compacted, a session that
|
|
60
|
+
dies at feature two of three — the plan is the only record of what "done"
|
|
61
|
+
meant. Prose in a reply is not.
|
|
62
|
+
|
|
63
|
+
The cost is the honest objection, and it is small: one feature on an existing
|
|
64
|
+
domain is ~10-15 lines of YAML, no `phase_0`, and no `flows` at all unless it
|
|
65
|
+
publishes or consumes events (`.agent/workflows/feature-plan.md`).
|
|
36
66
|
|
|
37
67
|
If you are the Planner, the first thing you write is `plans/active_plan.yaml`
|
|
38
68
|
itself — not a draft under another name that someone copies later. The copy
|
|
@@ -54,6 +84,7 @@ reviews a real file rather than a description of one.
|
|
|
54
84
|
| Phase 0 Builder | `microcoreos migrate`, `microcoreos schema` |
|
|
55
85
|
| Executor | none — write your two files and stop |
|
|
56
86
|
| Coordinator | `uv run -m pytest`, `microcoreos status`, and `microcoreos` (the real boot) for the final lint |
|
|
87
|
+
| Solo | All of the above — but each in its phase, and the real boot only at the end |
|
|
57
88
|
|
|
58
89
|
**Never `microcoreos run` / `uv run main.py` outside that last row.** It serves
|
|
59
90
|
forever: in the foreground it hangs your session, in the background it leaves a
|
|
@@ -157,10 +188,9 @@ The Kernel (ToolProxy & Container) is infrastructure-blind:
|
|
|
157
188
|
## 🔄 The pipeline, in five lines
|
|
158
189
|
|
|
159
190
|
The methodology, the phase numbering and the executor-prompt mechanics live in
|
|
160
|
-
`docs/PARALLEL_DEVELOPMENT.md` — **canonically, and only there**.
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
and no one compares two documents to notice.
|
|
191
|
+
`docs/PARALLEL_DEVELOPMENT.md` — **canonically, and only there**. What follows
|
|
192
|
+
is a five-line index, not a second copy: nobody diffs two documents, so a copy
|
|
193
|
+
here would keep prescribing a stale command with every test still green.
|
|
164
194
|
|
|
165
195
|
| Phase | Command | Gate |
|
|
166
196
|
|---|---|---|
|
|
@@ -87,22 +87,31 @@ Configuration Tool (config):
|
|
|
87
87
|
### 🔧 Tool: `telemetry` (Status: ✅)
|
|
88
88
|
```text
|
|
89
89
|
Telemetry Tool (telemetry):
|
|
90
|
-
- PURPOSE: OpenTelemetry distributed tracing. Auto-instruments all tool
|
|
91
|
-
No changes needed in plugins or existing tools to get basic
|
|
90
|
+
- PURPOSE: OpenTelemetry distributed tracing AND metrics. Auto-instruments all tool
|
|
91
|
+
calls via ToolProxy. No changes needed in plugins or existing tools to get basic
|
|
92
|
+
spans or metrics.
|
|
92
93
|
- ACTIVATION: Set OTEL_ENABLED=true. Degrades gracefully if disabled or packages missing.
|
|
93
94
|
- ENV VARS:
|
|
94
95
|
- OTEL_ENABLED: "true" to activate (default: "false").
|
|
95
|
-
- OTEL_SERVICE_NAME: Service name in traces (default: "microcoreos").
|
|
96
|
-
- OTEL_EXPORTER_OTLP_ENDPOINT: OTLP/gRPC endpoint (e.g. "http://
|
|
97
|
-
If not set, traces are printed to console (development mode).
|
|
96
|
+
- OTEL_SERVICE_NAME: Service name in traces/metrics (default: "microcoreos").
|
|
97
|
+
- OTEL_EXPORTER_OTLP_ENDPOINT: OTLP/gRPC endpoint (e.g. "http://otel-collector:4317").
|
|
98
|
+
If not set, traces and metrics are printed to console (development mode).
|
|
98
99
|
- CAPABILITIES:
|
|
99
100
|
- get_tracer(scope: str) -> Tracer: Named tracer for custom spans inside a plugin.
|
|
100
101
|
Usage: tracer = self.telemetry.get_tracer("my_plugin")
|
|
101
102
|
with tracer.start_as_current_span("my_operation"): ...
|
|
102
103
|
Returns a no-op tracer if OTel is disabled — safe to use unconditionally.
|
|
104
|
+
- get_meter(scope: str) -> Meter: Named meter for custom metrics inside a plugin.
|
|
105
|
+
Usage: meter = self.telemetry.get_meter("my_plugin")
|
|
106
|
+
counter = meter.create_counter("orders_created")
|
|
107
|
+
counter.add(1)
|
|
108
|
+
Returns a no-op meter if OTel is disabled — safe to use unconditionally.
|
|
103
109
|
- AUTO-INSTRUMENTATION (zero config):
|
|
104
110
|
Every tool call (db.execute, event_bus.publish, auth.create_token, etc.)
|
|
105
|
-
gets a span automatically via ToolProxy
|
|
111
|
+
gets a span automatically via ToolProxy, AND is recorded as an OTel histogram
|
|
112
|
+
(tool_call_duration_ms) and counter (tool_call_total) with tool/method/success
|
|
113
|
+
attributes — the same record already exposed at registry.get_metrics() / GET
|
|
114
|
+
/system/metrics, now also exported over OTLP. No plugin changes needed.
|
|
106
115
|
- DRIVER-LEVEL INSTRUMENTATION (optional, per tool):
|
|
107
116
|
Tools can implement on_instrument(tracer_provider) in BaseTool to add
|
|
108
117
|
framework-specific spans (SQL query text, HTTP route, etc.).
|
|
@@ -304,11 +313,11 @@ Async SQLite Persistence Tool (sqlite):
|
|
|
304
313
|
- `GET /system/events/schemas`
|
|
305
314
|
- **res**: EventSchemasData(schemas: dict)
|
|
306
315
|
- `GET /system/lint`
|
|
307
|
-
- **res**: SystemLintData(arch_violations: list[str], drift_warnings: list[str], event_contract_violations: list[LintFinding(code: str, severity: str, event: Optional[str], publisher: Optional[str], consumer: Optional[str], detail: str)], route_collisions: list[str], table_ownership_warnings: list[str], field_divergence_warnings: list[str])
|
|
316
|
+
- **res**: SystemLintData(arch_violations: list[str], drift_warnings: list[str], event_contract_violations: list[LintFinding(code: str, severity: str, event: Optional[str], publisher: Optional[str], consumer: Optional[str], detail: str)], route_collisions: list[str], table_ownership_warnings: list[str], field_divergence_warnings: list[str], dead_path_warnings: list[str])
|
|
308
317
|
- **Events emitted**: none
|
|
309
318
|
- **Events consumed**: none
|
|
310
319
|
- **Dependencies**: container, http, logger
|
|
311
|
-
- **Plugins**: devtools.DiscoveryNamingLinterPlugin, devtools.DomainIsolationLinterPlugin, devtools.EventContractLinterPlugin, devtools.EventSchemasPlugin, devtools.FieldDivergenceLinterPlugin, devtools.RouteCollisionLinterPlugin, devtools.TableOwnershipLinterPlugin, devtools.ToolDocDriftLinterPlugin
|
|
320
|
+
- **Plugins**: devtools.DiscoveryNamingLinterPlugin, devtools.DocPathLinterPlugin, devtools.DomainIsolationLinterPlugin, devtools.EventContractLinterPlugin, devtools.EventSchemasPlugin, devtools.FieldDivergenceLinterPlugin, devtools.RouteCollisionLinterPlugin, devtools.TableOwnershipLinterPlugin, devtools.ToolDocDriftLinterPlugin
|
|
312
321
|
|
|
313
322
|
### `system`
|
|
314
323
|
- **Tables**: none
|
|
@@ -27,9 +27,10 @@ These are the most frequent errors. Check these first before writing any code.
|
|
|
27
27
|
|
|
28
28
|
## ⚠️ The rules
|
|
29
29
|
|
|
30
|
-
The
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
The **Non-Negotiable Rules** in `AGENTS.md`, there and nowhere else. Never
|
|
31
|
+
restate them here, and never cite them with a count: a rival copy, or a number
|
|
32
|
+
that another file has to keep in step, is one more thing that can disagree with
|
|
33
|
+
the list an agent is standing in front of.
|
|
33
34
|
|
|
34
35
|
Event bus capabilities (`ttl`, `retries`, `backoff`, DLQ) are in `AI_CONTEXT.md`
|
|
35
36
|
§ Tool: `event_bus`, generated from the tool itself so they cannot drift.
|
|
@@ -63,7 +64,8 @@ domains/{name}/
|
|
|
63
64
|
|
|
64
65
|
`AI_CONTEXT.md` § **Plugin Authoring Guide** — one complete template per
|
|
65
66
|
deliverable type, regenerated on every boot from `tools/context/authoring_guide.md` and
|
|
66
|
-
already inside every executor prompt.
|
|
67
|
+
already inside every executor prompt. Never copy it here: the generated one
|
|
68
|
+
is the only one that tracks the code.
|
|
67
69
|
|
|
68
70
|
If you are writing a plugin you are an executor: nothing in this file is for you.
|
|
69
71
|
|
|
@@ -120,7 +122,9 @@ Rules of the pattern:
|
|
|
120
122
|
|
|
121
123
|
## 🔧 New Tool
|
|
122
124
|
|
|
123
|
-
**Location**: `tools/{name}/{name}_tool.py
|
|
125
|
+
**Location**: `tools/{name}/{name}_tool.py`, with its own tests in
|
|
126
|
+
`tools/{name}/tests/`, named `{name}_tool_test.py` — NEVER `test_{name}_tool.py`,
|
|
127
|
+
which the Kernel would import at boot. They travel with the tool when it is installed or swapped.
|
|
124
128
|
**Rule**: Stateless, isolated, self-documented. Use `EventBusDriver` pattern for new transport layers.
|
|
125
129
|
|
|
126
130
|
### The Parity Rule (Contract over Implementation)
|
|
@@ -130,9 +134,9 @@ replaces. This ensures that plugins remain infrastructure-blind and behavior
|
|
|
130
134
|
is consistent across backends.
|
|
131
135
|
|
|
132
136
|
**Canonical examples:**
|
|
133
|
-
- `tests/tools/test_state_parity.py`: Verifies that `RedisStateTool` behaves
|
|
137
|
+
- `tests/tools/state/test_state_parity.py`: Verifies that `RedisStateTool` behaves
|
|
134
138
|
exactly like the default in-memory `StateTool`.
|
|
135
|
-
- `tests/tools/test_event_bus_broker_parity.py`: Parametrized suite that
|
|
139
|
+
- `tests/tools/event_bus/test_event_bus_broker_parity.py`: Parametrized suite that
|
|
136
140
|
runs against both the local driver and `RedisStreamsDriver`.
|
|
137
141
|
|
|
138
142
|
**Health contract (optional, only for tools with an external backend)**:
|
|
@@ -261,15 +265,15 @@ plugin against a contract the test itself declares, so if a tool's real API
|
|
|
261
265
|
drifts the mock keeps passing. That gap is closed elsewhere, not by your test:
|
|
262
266
|
the `ToolDocDriftLinter` (boot + `/system/lint`) compares each tool's
|
|
263
267
|
documented interface against its implementation, and the parity suites
|
|
264
|
-
(`tests/tools/test_state_parity.py`,
|
|
265
|
-
`tests/tools/test_event_bus_broker_parity.py`) hold swappable tools to one
|
|
268
|
+
(`tests/tools/state/test_state_parity.py`,
|
|
269
|
+
`tests/tools/event_bus/test_event_bus_broker_parity.py`) hold swappable tools to one
|
|
266
270
|
behaviour. Do not re-verify that in a plugin test.
|
|
267
271
|
|
|
268
272
|
### Ask for tools the way the plugin does
|
|
269
273
|
|
|
270
274
|
A plugin never builds a tool: it names one in `__init__` and the Kernel hands
|
|
271
275
|
it over. pytest injects by parameter name too, so **a test uses the same
|
|
272
|
-
vocabulary** — `
|
|
276
|
+
vocabulary** — `conftest.py` (repo root) publishes one fixture per Kernel injection
|
|
273
277
|
key (`db`, `event_bus`, `auth`, `state`, `logger`, `config`). No tool imports,
|
|
274
278
|
no setup/teardown, no hand-built schema:
|
|
275
279
|
|
|
@@ -285,7 +289,7 @@ async def test_create_user_persists(db, event_bus, auth, logger):
|
|
|
285
289
|
assert rows[0]["password_hash"] != "secret123"
|
|
286
290
|
```
|
|
287
291
|
|
|
288
|
-
Worked example: `tests/test_plugin_di_fixtures.py`.
|
|
292
|
+
Worked example: `tests/core/test_plugin_di_fixtures.py`.
|
|
289
293
|
|
|
290
294
|
- `@pytest.mark.migrations("users", "system")` applies those domains' real
|
|
291
295
|
migration files. **No marker = a real but empty database**, which is what a
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: microcoreos
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Atomic Microkernel Architecture optimized for AI-Driven Development
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -184,7 +184,7 @@ That is the code your app depends on, and it is the whole of it: booting loads
|
|
|
184
184
|
`kernel`, `container`, `registry`, `context` and the two base classes, and
|
|
185
185
|
nothing else. The rest of the package — the scaffolder, the extras catalog,
|
|
186
186
|
`upgrade` — is the `microcoreos` command, build-time work that never executes
|
|
187
|
-
inside your application. `tests/test_core_purity.py` enforces both halves of
|
|
187
|
+
inside your application. `tests/core/test_core_purity.py` enforces both halves of
|
|
188
188
|
that: a third-party import in the kernel fails the suite, and so does the
|
|
189
189
|
kernel importing the CLI half.
|
|
190
190
|
|
|
@@ -217,7 +217,7 @@ This pattern works for any infrastructure: swap the event bus backend, the HTTP
|
|
|
217
217
|
|
|
218
218
|
Additional tools — PostgreSQL, Redis state, auth, S3, the scheduler, chaos — ship in `extras/` and install with `microcoreos add <name>`. If the new tool reuses an existing `name` (e.g. `redis_state` registers as `"state"`), move the tool it replaces out of `tools/` first: only one tool per name may be discovered.
|
|
219
219
|
|
|
220
|
-
The Redis state swap is verified by a parity suite (`tests/tools/test_state_parity.py`): the same contract battery runs against the in-memory reference and against a real Redis, so the replacement is proven equivalent, not assumed.
|
|
220
|
+
The Redis state swap is verified by a parity suite (`tests/tools/state/test_state_parity.py`): the same contract battery runs against the in-memory reference and against a real Redis, so the replacement is proven equivalent, not assumed.
|
|
221
221
|
|
|
222
222
|
### Honest Kernel & Smart Infrastructure.
|
|
223
223
|
|
|
@@ -231,7 +231,7 @@ The Kernel is "logic-free." `ToolProxy` observes and reports health but **never
|
|
|
231
231
|
|
|
232
232
|
**Tool Call Metrics** — Every tool method call is automatically timed by ToolProxy. Access via `registry.get_metrics()` or attach a real-time sink.
|
|
233
233
|
|
|
234
|
-
**OpenTelemetry** (optional) — Set `OTEL_ENABLED=true`. Every tool call gets a span. Export to Jaeger, Grafana Tempo, Datadog. Zero changes to plugins.
|
|
234
|
+
**OpenTelemetry** (optional) — Set `OTEL_ENABLED=true`. Every tool call gets a span AND is recorded as a histogram (`tool_call_duration_ms`) + counter (`tool_call_total`). Export to Jaeger, Grafana Tempo, Prometheus, Datadog. Zero changes to plugins.
|
|
235
235
|
|
|
236
236
|
---
|
|
237
237
|
|
|
@@ -418,6 +418,23 @@ Two tracks — see [ROADMAP.md](ROADMAP.md) for the full plan and decision log:
|
|
|
418
418
|
|
|
419
419
|
---
|
|
420
420
|
|
|
421
|
+
## 💖 Built on the Shoulders of Open-Source Giants
|
|
422
|
+
|
|
423
|
+
MicroCoreOS provides the **Atomic Microkernel Architecture, DI container, orchestration engine, and 1-file decoupling**. Every swappable Tool is a contract wrapper powered by world-class open-source projects created by amazing maintainers:
|
|
424
|
+
|
|
425
|
+
- **FastAPI, Starlette & Uvicorn** — Powers `HttpServerTool` (`http`)
|
|
426
|
+
- **Pydantic** — Powers request/response schema validation
|
|
427
|
+
- **aiosqlite & asyncpg** — Powers `SQLiteTool` and `PostgreSQLTool` (`db`)
|
|
428
|
+
- **redis-py** — Powers `RedisStateTool` and Redis Streams event bus driver
|
|
429
|
+
- **aiokafka & aio-pika** — Powers Kafka and RabbitMQ event bus drivers
|
|
430
|
+
- **APScheduler** — Powers `SchedulerTool` (`scheduler`)
|
|
431
|
+
- **PyJWT & bcrypt** — Powers `AuthTool` (`auth`)
|
|
432
|
+
- **OpenTelemetry** — Powers `TelemetryTool` (`telemetry`)
|
|
433
|
+
- **aioboto3** — Powers `S3Tool` (`s3`)
|
|
434
|
+
- **mutmut & Locust** — Powers mutation blindage and the `MicroCoreBench` laboratory
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
421
438
|
## License
|
|
422
439
|
|
|
423
440
|
[MIT](LICENSE)
|