microcoreos 0.2.0__tar.gz → 0.2.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.2.2/.agent/skills/microcoreos-architecture/SKILL.md +34 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/.agent/skills/microcoreos-architecture/agent.md +1 -1
- {microcoreos-0.2.0 → microcoreos-0.2.2}/.agent/workflows/feature-plan.md +34 -9
- microcoreos-0.2.2/.agent/workflows/multi-domain-plan.md +88 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/.agent/workflows/new-domain.md +41 -73
- {microcoreos-0.2.0 → microcoreos-0.2.2}/AGENTS.md +104 -65
- {microcoreos-0.2.0 → microcoreos-0.2.2}/AI_CONTEXT.md +32 -9
- {microcoreos-0.2.0 → microcoreos-0.2.2}/INSTRUCTIONS_FOR_AI.md +12 -53
- {microcoreos-0.2.0 → microcoreos-0.2.2}/PKG-INFO +12 -2
- {microcoreos-0.2.0 → microcoreos-0.2.2}/README.md +11 -1
- {microcoreos-0.2.0 → microcoreos-0.2.2}/ROADMAP.md +1 -1
- {microcoreos-0.2.0 → microcoreos-0.2.2}/docs/CLI.md +101 -4
- {microcoreos-0.2.0 → microcoreos-0.2.2}/docs/INDEX.md +2 -2
- {microcoreos-0.2.0 → microcoreos-0.2.2}/docs/PARALLEL_DEVELOPMENT.md +21 -12
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_tools/redis_state/redis_state_tool.py +1 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_tools/s3/s3_tool.py +1 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/microcoreos/cli.py +65 -71
- microcoreos-0.2.2/microcoreos/project.py +97 -0
- microcoreos-0.2.2/microcoreos/scaffold.py +449 -0
- microcoreos-0.2.2/microcoreos_dev/__init__.py +11 -0
- microcoreos-0.2.2/microcoreos_dev/cli.py +75 -0
- microcoreos-0.2.0/dev_infra/plan_fuzzer.py → microcoreos-0.2.2/microcoreos_dev/fuzzer.py +49 -30
- microcoreos-0.2.2/microcoreos_dev/pipeline.py +526 -0
- microcoreos-0.2.2/microcoreos_dev/plan/__init__.py +68 -0
- microcoreos-0.2.0/domains/devtools/plugins/plan_validator_plugin.py → microcoreos-0.2.2/microcoreos_dev/plan/rules.py +375 -492
- microcoreos-0.2.2/microcoreos_dev/plan/scan.py +241 -0
- microcoreos-0.2.2/microcoreos_dev/plan/schema.py +202 -0
- microcoreos-0.2.2/microcoreos_dev/probe.py +247 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/plans/README.md +55 -20
- {microcoreos-0.2.0 → microcoreos-0.2.2}/plans/active_plan.md +13 -0
- microcoreos-0.2.2/plans/active_plan.yaml +119 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/pyproject.toml +23 -7
- microcoreos-0.2.2/tests/dev/corpus/README.md +14 -0
- microcoreos-0.2.2/tests/dev/corpus/qwen_twitter_plan.yaml +136 -0
- microcoreos-0.2.2/tests/dev/test_pipeline.py +678 -0
- {microcoreos-0.2.0/tests → microcoreos-0.2.2/tests/dev}/test_plan_validator.py +572 -124
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_cli.py +91 -5
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_context_tool.py +57 -0
- microcoreos-0.2.2/tests/test_core_purity.py +183 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_scaffold.py +131 -7
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tools/context/authoring_guide.md +31 -5
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tools/context/context_tool.py +15 -3
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tools/sqlite/sqlite_tool.py +1 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tools/state/state_tool.py +1 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/uv.lock +1 -1
- microcoreos-0.2.0/.agent/skills/microcoreos-architecture/SKILL.md +0 -24
- microcoreos-0.2.0/.agent/workflows/multi-domain-plan.md +0 -96
- microcoreos-0.2.0/docs/PLAN_EVENT_LINTER.md +0 -95
- microcoreos-0.2.0/docs/RELEASING.md +0 -108
- microcoreos-0.2.0/docs/TECH_DEBT.md +0 -414
- microcoreos-0.2.0/microcoreos/scaffold.py +0 -220
- microcoreos-0.2.0/plans/PILOT.md +0 -99
- microcoreos-0.2.0/plans/active_plan.yaml +0 -48
- microcoreos-0.2.0/tests/test_core_purity.py +0 -97
- {microcoreos-0.2.0 → microcoreos-0.2.2}/.agent/workflows/new-tool.md +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/.claude/settings.local.json +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/.dockerignore +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/.env.example +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/.github/workflows/ci.yml +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/.github/workflows/release.yml +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/.gitignore +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/.python-version +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/Dockerfile +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/LICENSE +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/cli.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/dev_infra/cache_probe.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/dev_infra/docker-compose.yml +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/docs/CORE_INFRASTRUCTURE.md +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/docs/ELASTIC_DEPLOYMENT.md +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/docs/EVENT_BUS.md +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/docs/HTTP_SERVER.md +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/docs/OBSERVABILITY.md +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/docs/OBSERVABILITY_API.md +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/docs/translations/es/README.md +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/domains/devtools/lint/plugin_sources.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/domains/devtools/plugins/discovery_naming_linter_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/domains/devtools/plugins/domain_isolation_linter_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/domains/devtools/plugins/event_contract_linter_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/domains/devtools/plugins/event_schemas_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/domains/devtools/plugins/field_divergence_linter_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/domains/devtools/plugins/route_collision_linter_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/domains/devtools/plugins/table_ownership_linter_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/domains/devtools/plugins/tool_doc_drift_linter_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/domains/system/plugins/event_delivery_monitor_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/domains/system/plugins/system_events_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/domains/system/plugins/system_events_stream_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/domains/system/plugins/system_logs_stream_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/domains/system/plugins/system_metrics_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/domains/system/plugins/system_status_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/domains/system/plugins/system_traces_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/domains/system/plugins/system_traces_stream_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/domains/system/plugins/tool_health_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_domains/chaos/plugins/blocking_boot_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_domains/chaos/plugins/chaos_control_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_domains/chaos/plugins/failing_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_domains/chaos/plugins/stress_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_domains/ping/plugins/ping_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_domains/scheduler/migrations/001_scheduler_one_shots.sql +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_domains/scheduler/models/scheduler_one_shot.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_domains/scheduler/plugins/durable_one_shots_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_domains/users/migrations/001_create_users.sql +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_domains/users/models/user.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_domains/users/plugins/create_user_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_domains/users/plugins/delete_user_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_domains/users/plugins/get_me_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_domains/users/plugins/get_user_by_id_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_domains/users/plugins/get_users_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_domains/users/plugins/login_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_domains/users/plugins/logout_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_domains/users/plugins/update_user_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_domains/users/plugins/welcome_service_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_tools/auth/auth_tool.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_tools/chaos/chaos_tool.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_tools/kafka/kafka_driver.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_tools/postgresql/postgresql_tool.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_tools/rabbitmq/rabbitmq_driver.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_tools/s3/__init__.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/extras/available_tools/scheduler/scheduler_tool.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/hatch_build.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/main.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/microcoreos/__init__.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/microcoreos/base_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/microcoreos/base_tool.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/microcoreos/catalog.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/microcoreos/container.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/microcoreos/context.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/microcoreos/kernel.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/microcoreos/project_readme.md +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/microcoreos/registry.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/microcoreos/upgrade.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/conftest.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/domains/ping/test_ping_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/domains/users/test_create_user_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/domains/users/test_delete_user_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/domains/users/test_get_me_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/domains/users/test_get_user_by_id_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/domains/users/test_get_users_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/domains/users/test_login_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/domains/users/test_logout_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/domains/users/test_update_user_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/domains/users/test_welcome_service_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/helpers/active_db.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/helpers/async_wait.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/helpers/mock_db.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/helpers/trace_chains.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_auth_tool.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_catalog.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_chaos_control.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_chaos_tool.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_config_tool.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_core.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_discovery_naming_linter.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_domain_isolation_linter.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_durable_one_shots.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_event_bus_groups.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_event_bus_tool.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_event_contract_linter.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_event_schemas_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_field_divergence_linter.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_http_params_hardening.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_http_server_tool.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_kernel.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_logger_tool.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_no_retry.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_plugin_di_fixtures.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_postgresql_describe_schema.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_postgresql_tool.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_registry_collisions.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_route_collision_linter.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_s3_tool.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_scheduler_singleton.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_scheduler_tool.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_security_hardening.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_sqlite_concurrency.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_sqlite_describe_schema.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_sqlite_migrations.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_sqlite_tool.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_state_tool.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_system_events_stats.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_system_traces_plugin.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_table_ownership_linter.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_tool_doc_drift_linter.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_tool_proxy.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_trace_chain_helper.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/test_upgrade.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/tools/test_db_parity.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/tools/test_event_bus_broker_parity.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/tools/test_event_bus_kafka_parity.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/tools/test_event_bus_rabbitmq_parity.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/tools/test_redis_streams_driver.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/tools/test_s3_parity.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/tools/test_sqlite_driver.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tests/tools/test_state_parity.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tools/config/config_tool.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tools/context/renderers.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tools/context/scanners.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tools/event_bus/drivers.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tools/event_bus/envelope.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tools/event_bus/event_bus_tool.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tools/event_bus/redis_streams_driver.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tools/event_bus/sqlite_driver.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tools/http_server/context.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tools/http_server/http_server_tool.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tools/http_server/pipeline.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tools/logger/logger_tool.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tools/sqlite/errors.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tools/sqlite/migrations.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tools/sqlite/transaction.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tools/system/registry_tool.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tools/telemetry/__init__.py +0 -0
- {microcoreos-0.2.0 → microcoreos-0.2.2}/tools/telemetry/telemetry_tool.py +0 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: microcoreos-architecture
|
|
3
|
+
description: Ensures adherence to MicroCoreOS "Atomic Microkernel" architecture. Use when creating or modifying core, tools, plugins, or domains.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# MicroCoreOS Architecture Skill
|
|
7
|
+
|
|
8
|
+
**Read `AGENTS.md` and follow the route for your role.** That table names the
|
|
9
|
+
one or two files your role needs and, just as importantly, the ones it must not
|
|
10
|
+
open — a Planner that also loads the plugin templates spends ~3,000 tokens on
|
|
11
|
+
code it will never write.
|
|
12
|
+
|
|
13
|
+
This file deliberately holds no rules, no reading path and no checklist of its
|
|
14
|
+
own. It used to hold all three, and all three had drifted: its reading path
|
|
15
|
+
sent plugin authors to `INSTRUCTIONS_FOR_AI.md` for templates that live in the
|
|
16
|
+
generated manifest, and its checklist was a fifth partial copy of the 13
|
|
17
|
+
Non-Negotiable Rules — one that never mentioned typed event payloads.
|
|
18
|
+
|
|
19
|
+
| You need | It is in |
|
|
20
|
+
|---|---|
|
|
21
|
+
| The rules | `AGENTS.md` § Non-Negotiable Rules (13, canonical) |
|
|
22
|
+
| Kernel/tool/event-bus laws | `AGENTS.md` § Core Architectural Laws |
|
|
23
|
+
| The plugin template | `AI_CONTEXT.md` § Plugin Authoring Guide (regenerated every boot) |
|
|
24
|
+
| What exists right now | `AI_CONTEXT.md` § Available Tools / § Domains |
|
|
25
|
+
| The plan format and its 18 rules | `docs/PARALLEL_DEVELOPMENT.md` § Phase 1 |
|
|
26
|
+
| Anti-patterns, testing, building a tool | `INSTRUCTIONS_FOR_AI.md` |
|
|
27
|
+
|
|
28
|
+
Before you finish, the gates are commands, not a checklist to eyeball:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
microcoreos plan validate # the plan is a contract — zero errors
|
|
32
|
+
uv run -m pytest # green
|
|
33
|
+
microcoreos status # manifest still describes the code on disk
|
|
34
|
+
```
|
|
@@ -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
|
-
- **
|
|
13
|
+
- **Rules Review**: Before implementing, check the 13 Non-Negotiable Rules in `AGENTS.md`. (This line used to cite "Three Golden Rules in SKILL.md" — a document that never existed.)
|
|
14
14
|
- **Resilience**: Will a failure here crash the entire system?
|
|
15
15
|
- **Observability**: Can this be monitored via the `registry`?
|
|
16
16
|
|
|
@@ -8,11 +8,36 @@ The smallest planning level: new plugins on a domain that already exists. No
|
|
|
8
8
|
migrations, no new tools — if you need either, escalate to
|
|
9
9
|
[new-domain.md](new-domain.md) or [multi-domain-plan.md](multi-domain-plan.md).
|
|
10
10
|
|
|
11
|
+
## Before you plan — read these two, in this order
|
|
12
|
+
|
|
13
|
+
1. **`plans/active_plan.yaml`** — the file you are about to overwrite. It ships
|
|
14
|
+
as a worked example of all three feature shapes. It **is** the format, not a
|
|
15
|
+
description of one, and it is the cheapest way to have it.
|
|
16
|
+
2. **`AI_CONTEXT.md`**, down to `## 🧩 Plugin Authoring Guide` — the tables,
|
|
17
|
+
models, routes and events that already exist. Inherit their names exactly.
|
|
18
|
+
|
|
19
|
+
Then write to **`plans/active_plan.yaml`** — that exact path, overwriting it —
|
|
20
|
+
and run `microcoreos plan validate` until it reports zero errors. Errors carry
|
|
21
|
+
the YAML that fixes them: paste it.
|
|
22
|
+
|
|
23
|
+
**Write the file before you ask anything.** Checking in is fine — planning
|
|
24
|
+
often is a conversation — but never *instead of* writing: a plan that exists
|
|
25
|
+
only as prose in your reply is a plan the next phase cannot read, and the run
|
|
26
|
+
may not be interactive at all. So write the YAML, validate it, and then raise
|
|
27
|
+
whatever you wanted to raise; the operator answers against a real file instead
|
|
28
|
+
of a description. Where a detail is genuinely undecidable, take what the
|
|
29
|
+
existing vocabulary implies, put it in the YAML, and flag it in a comment.
|
|
30
|
+
|
|
31
|
+
`docs/PARALLEL_DEVELOPMENT.md` § Phase 1 holds the rules behind the format.
|
|
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. A
|
|
34
|
+
planner that did produced a plan with every field renamed.
|
|
35
|
+
|
|
11
36
|
## Prerequisites
|
|
12
37
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
38
|
+
Both files above. For an event you will consume, its payload contract is the
|
|
39
|
+
"Events emitted" line of the publishing domain in `AI_CONTEXT.md` (or
|
|
40
|
+
`GET /system/events/schemas` on a running system).
|
|
16
41
|
|
|
17
42
|
## Steps
|
|
18
43
|
|
|
@@ -37,7 +62,7 @@ plan:
|
|
|
37
62
|
model: OrderCancelledPayload
|
|
38
63
|
payload: { id: int, reason: str }
|
|
39
64
|
consumes: []
|
|
40
|
-
|
|
65
|
+
tools: [http, db, event_bus, logger] # every tool __init__ takes
|
|
41
66
|
test: tests/test_cancel_order.py
|
|
42
67
|
flows:
|
|
43
68
|
- name: order-cancellation
|
|
@@ -60,10 +85,10 @@ plan:
|
|
|
60
85
|
|
|
61
86
|
### 2. Validate before writing code
|
|
62
87
|
|
|
63
|
-
`
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
things it catches at this level:
|
|
88
|
+
`microcoreos plan validate` — it runs the 18 validity rules of
|
|
89
|
+
`docs/PARALLEL_DEVELOPMENT.md` against this plan AND what the repo already
|
|
90
|
+
occupies, with no server running. Zero `errors` before any code; `warnings` are
|
|
91
|
+
advisory. The main things it catches at this level:
|
|
67
92
|
|
|
68
93
|
- The `route` and `file` collide with nothing live.
|
|
69
94
|
- Every consumed event exists (live system or this plan) and provides the
|
|
@@ -82,7 +107,7 @@ Publish with `XxxPayload(...).model_dump()` — bare call, no arguments.
|
|
|
82
107
|
|
|
83
108
|
- One test per plugin proving the black-box contract: input → output, DB
|
|
84
109
|
effects on the declared tables, published payloads with the declared fields.
|
|
85
|
-
Mock exactly the tools the plan's `
|
|
110
|
+
Mock exactly the tools the plan's `tools:` lists; run the rest as real
|
|
86
111
|
in-memory instances (`INSTRUCTIONS_FOR_AI.md` § Testing).
|
|
87
112
|
- One double-delivery test per idempotent link (same envelope twice → same
|
|
88
113
|
final state), at the path declared in `idempotency_test`.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Plan and build a large spec spanning multiple domains (full formal plan, parallel execution)
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Multi-Domain Plan Workflow
|
|
6
|
+
|
|
7
|
+
The largest planning level: a spec that creates or touches several domains,
|
|
8
|
+
with event chains crossing domain boundaries. The methodology is fully
|
|
9
|
+
specified in `docs/PARALLEL_DEVELOPMENT.md` — this workflow is its checklist.
|
|
10
|
+
|
|
11
|
+
**Everything is decided before any code exists**: every migration, model,
|
|
12
|
+
tool, plugin, route, event (with its payload model), and every chain with its
|
|
13
|
+
happy path and sad paths. Code-time conflicts are structurally impossible;
|
|
14
|
+
what remains is getting the plan right.
|
|
15
|
+
|
|
16
|
+
## Before you plan — read these two, in this order
|
|
17
|
+
|
|
18
|
+
1. **`plans/active_plan.yaml`** — the file you are about to overwrite. It ships
|
|
19
|
+
as a worked example of all three feature shapes. It **is** the format, not a
|
|
20
|
+
description of one, and it is the cheapest way to have it.
|
|
21
|
+
2. **`AI_CONTEXT.md`**, down to `## 🧩 Plugin Authoring Guide` — the tables,
|
|
22
|
+
models, routes and events that already exist. Inherit their names exactly.
|
|
23
|
+
|
|
24
|
+
Then write to **`plans/active_plan.yaml`** — that exact path, overwriting it —
|
|
25
|
+
and run `microcoreos plan validate` until it reports zero errors. Errors carry
|
|
26
|
+
the YAML that fixes them: paste it.
|
|
27
|
+
|
|
28
|
+
**Write the file before you ask anything.** Checking in is fine — planning
|
|
29
|
+
often is a conversation — but never *instead of* writing: a plan that exists
|
|
30
|
+
only as prose in your reply is a plan the next phase cannot read, and the run
|
|
31
|
+
may not be interactive at all. So write the YAML, validate it, and then raise
|
|
32
|
+
whatever you wanted to raise; the operator answers against a real file instead
|
|
33
|
+
of a description. Where a detail is genuinely undecidable, take what the
|
|
34
|
+
existing vocabulary implies, put it in the YAML, and flag it in a comment.
|
|
35
|
+
|
|
36
|
+
`docs/PARALLEL_DEVELOPMENT.md` § Phase 1 holds the rules behind the format.
|
|
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. A
|
|
39
|
+
planner that did produced a plan with every field renamed.
|
|
40
|
+
|
|
41
|
+
## Phase 1 — The full plan (the contract, authored FIRST)
|
|
42
|
+
|
|
43
|
+
Write the complete YAML plan of `docs/PARALLEL_DEVELOPMENT.md` ("Formal plan
|
|
44
|
+
format") to `plans/active_plan.yaml`: `phase_0` (every migration with its
|
|
45
|
+
`tables:` ownership AND its full `columns:` — phase 0 is built from the plan,
|
|
46
|
+
nothing is improvised later), `features` (one per plugin, with
|
|
47
|
+
`publishes.model` / `consumes.requires` / the `db:` persistence contract),
|
|
48
|
+
and `flows` — each with
|
|
49
|
+
its `durability` (may in-flight events die with the process? `durable` needs
|
|
50
|
+
the sqlite/redis driver) and the sad-path checklist per link:
|
|
51
|
+
|
|
52
|
+
- `retries` / `backoff` — re-delivery policy
|
|
53
|
+
- `idempotent` — MANDATORY `true` where `retries > 0` OR the flow is `durable`
|
|
54
|
+
(durable transports re-deliver after a crash even with zero retries)
|
|
55
|
+
- `idempotency_test` — the double-delivery proof for every idempotent link
|
|
56
|
+
- `dlq_watcher` — who consumes `_dlq.<event>` (`null` = loss explicitly
|
|
57
|
+
accepted; a non-null watcher must exist in the plan or live)
|
|
58
|
+
- `atomic_with_db` — `true` means this chain cannot lose the event between DB
|
|
59
|
+
commit and publish → it is the implementation trigger for the Transactional
|
|
60
|
+
Outbox (ROADMAP Issue 28); flag it, do not improvise one
|
|
61
|
+
- `compensation` — the event that undoes upstream work if the chain dies
|
|
62
|
+
(saga); it must be published AND consumed within the plan
|
|
63
|
+
- `sad_path_test` (flow-level) — mandatory when any link declares retries,
|
|
64
|
+
a DLQ watcher or a compensation
|
|
65
|
+
- `rpc_links` (flow-level) — every `request()` call, with `timeout` and
|
|
66
|
+
`on_timeout`
|
|
67
|
+
|
|
68
|
+
Then run the 18 validity rules mechanically: `microcoreos plan validate`
|
|
69
|
+
(offline — nothing needs to be booted) — zero `errors` before building
|
|
70
|
+
anything. An invalid plan is a task-allocation error — fix the plan,
|
|
71
|
+
never patch it in code.
|
|
72
|
+
|
|
73
|
+
## Phases 0, 2 and 3
|
|
74
|
+
|
|
75
|
+
Identical to any other plan — `docs/PARALLEL_DEVELOPMENT.md` owns them, and
|
|
76
|
+
restating them here is how this file once kept prescribing a boot command that
|
|
77
|
+
had stopped regenerating the manifest.
|
|
78
|
+
|
|
79
|
+
Two things are specific to multi-domain work and are the only reason this
|
|
80
|
+
section exists:
|
|
81
|
+
|
|
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
|
|
84
|
+
exist before another's. The db tool resolves the order and prints each file
|
|
85
|
+
as it applies it.
|
|
86
|
+
- **Never assign two agents to the same feature**, and never let one agent
|
|
87
|
+
touch two domains. The wave is safe precisely because the write sets are
|
|
88
|
+
disjoint.
|
|
@@ -10,9 +10,36 @@ Creates a full domain from scratch: entity model, SQL migration, and one plugin
|
|
|
10
10
|
> one new domain → this workflow · several domains / cross-domain chains →
|
|
11
11
|
> [multi-domain-plan.md](multi-domain-plan.md) · new infrastructure → [new-tool.md](new-tool.md).
|
|
12
12
|
|
|
13
|
+
## Before you plan — read these two, in this order
|
|
14
|
+
|
|
15
|
+
1. **`plans/active_plan.yaml`** — the file you are about to overwrite. It ships
|
|
16
|
+
as a worked example of all three feature shapes. It **is** the format, not a
|
|
17
|
+
description of one, and it is the cheapest way to have it.
|
|
18
|
+
2. **`AI_CONTEXT.md`**, down to `## 🧩 Plugin Authoring Guide` — the tables,
|
|
19
|
+
models, routes and events that already exist. Inherit their names exactly.
|
|
20
|
+
|
|
21
|
+
Then write to **`plans/active_plan.yaml`** — that exact path, overwriting it —
|
|
22
|
+
and run `microcoreos plan validate` until it reports zero errors. Errors carry
|
|
23
|
+
the YAML that fixes them: paste it.
|
|
24
|
+
|
|
25
|
+
**Write the file before you ask anything.** Checking in is fine — planning
|
|
26
|
+
often is a conversation — but never *instead of* writing: a plan that exists
|
|
27
|
+
only as prose in your reply is a plan the next phase cannot read, and the run
|
|
28
|
+
may not be interactive at all. So write the YAML, validate it, and then raise
|
|
29
|
+
whatever you wanted to raise; the operator answers against a real file instead
|
|
30
|
+
of a description. Where a detail is genuinely undecidable, take what the
|
|
31
|
+
existing vocabulary implies, put it in the YAML, and flag it in a comment.
|
|
32
|
+
|
|
33
|
+
`docs/PARALLEL_DEVELOPMENT.md` § Phase 1 holds the rules behind the format.
|
|
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. A
|
|
36
|
+
planner that did produced a plan with every field renamed.
|
|
37
|
+
|
|
13
38
|
## Prerequisites
|
|
14
|
-
|
|
15
|
-
|
|
39
|
+
|
|
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 13
|
|
42
|
+
Non-Negotiable Rules in `AGENTS.md`.
|
|
16
43
|
|
|
17
44
|
## Steps
|
|
18
45
|
|
|
@@ -28,7 +55,8 @@ persistence contract), and — ONLY if any plugin publishes or consumes events
|
|
|
28
55
|
the `atomic_with_db` outbox question — and the declared `idempotency_test` /
|
|
29
56
|
`sad_path_test` files). A pure-CRUD domain has no `flows` section at all; a
|
|
30
57
|
domain whose delete cascades through one event has exactly one flow.
|
|
31
|
-
Validate with `
|
|
58
|
+
Validate with `microcoreos plan validate` before writing code (offline —
|
|
59
|
+
nothing needs to be booted). Build in that
|
|
32
60
|
order — tools first if any, then migrations + models, then plugins with their
|
|
33
61
|
events. Nothing below this line should require a decision the plan did not
|
|
34
62
|
already make. Expected size for a CRUD domain with one event chain: ~80-120
|
|
@@ -77,75 +105,14 @@ CREATE TABLE IF NOT EXISTS {name}s (
|
|
|
77
105
|
|
|
78
106
|
### 4. Create plugins (1 file = 1 use case)
|
|
79
107
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
**
|
|
83
|
-
|
|
84
|
-
- Define the **response schema** (what the HTTP client receives) at the **top of the plugin file** too — never import the Entity for this; only expose the fields you actually return.
|
|
85
|
-
- Define the **event payload schema** for every event this plugin publishes, also at the top of the file: `{Name}CreatedPayload(BaseModel)`. Publish with `.model_dump()` (bare call, no arguments). The publisher owns the event contract — consumers in other domains never import it; they declare their own model with only the fields they need (tolerant reader).
|
|
86
|
-
- Always pass `response_model=` to `add_endpoint` — this generates complete OpenAPI docs.
|
|
87
|
-
|
|
88
|
-
Example for create:
|
|
89
|
-
|
|
90
|
-
File: `domains/{name}/plugins/create_{name}_plugin.py`
|
|
91
|
-
|
|
92
|
-
```python
|
|
93
|
-
from typing import Optional
|
|
94
|
-
from pydantic import BaseModel
|
|
95
|
-
from microcoreos import BasePlugin
|
|
96
|
-
|
|
97
|
-
# ── Request schema lives HERE ──────────────────────
|
|
98
|
-
class Create{Name}Request(BaseModel):
|
|
99
|
-
# Only input fields — no id, no internal fields
|
|
100
|
-
field1: str
|
|
101
|
-
field2: int
|
|
102
|
-
|
|
103
|
-
# ── Response schema lives HERE ─────────────────────
|
|
104
|
-
class {Name}Data(BaseModel):
|
|
105
|
-
id: int
|
|
106
|
-
field1: str
|
|
107
|
-
field2: int
|
|
108
|
-
|
|
109
|
-
class Create{Name}Response(BaseModel):
|
|
110
|
-
success: bool
|
|
111
|
-
data: Optional[{Name}Data] = None
|
|
112
|
-
error: Optional[str] = None
|
|
113
|
-
|
|
114
|
-
# ── Event payload schema lives HERE (publisher owns the contract) ──
|
|
115
|
-
class {Name}CreatedPayload(BaseModel):
|
|
116
|
-
id: int
|
|
117
|
-
|
|
118
|
-
class Create{Name}Plugin(BasePlugin):
|
|
119
|
-
def __init__(self, http, db, event_bus, logger):
|
|
120
|
-
self.http = http
|
|
121
|
-
self.db = db
|
|
122
|
-
self.bus = event_bus
|
|
123
|
-
self.logger = logger
|
|
124
|
-
|
|
125
|
-
async def on_boot(self):
|
|
126
|
-
self.http.add_endpoint(
|
|
127
|
-
"/{name}s", "POST", self.execute,
|
|
128
|
-
tags=["{Name}s"], request_model=Create{Name}Request,
|
|
129
|
-
response_model=Create{Name}Response,
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
async def execute(self, data: dict, context=None):
|
|
133
|
-
try:
|
|
134
|
-
req = Create{Name}Request(**data)
|
|
135
|
-
new_id = await self.db.execute(
|
|
136
|
-
"INSERT INTO {name}s (field1, field2) VALUES ($1, $2) RETURNING id",
|
|
137
|
-
[req.field1, req.field2]
|
|
138
|
-
)
|
|
139
|
-
self.logger.info(f"{Name} created with ID {new_id}")
|
|
140
|
-
await self.bus.publish("{name}.created", {Name}CreatedPayload(id=new_id).model_dump())
|
|
141
|
-
return {"success": True, "data": {"id": new_id, "field1": req.field1, "field2": req.field2}}
|
|
142
|
-
except Exception as e:
|
|
143
|
-
# Safe Error Reporting: log technically, respond safely (never str(e)).
|
|
144
|
-
self.logger.error(f"Failed to create {name}: {e}")
|
|
145
|
-
return {"success": False, "error": "Database operation failed"}
|
|
146
|
-
```
|
|
108
|
+
One plugin file per operation in `domains/{name}/plugins/`. The template — with
|
|
109
|
+
request, response and event-payload schemas inline, and the rules that go with
|
|
110
|
+
them — is `AI_CONTEXT.md` § **Plugin Authoring Guide**, regenerated on every
|
|
111
|
+
boot and already inside every executor prompt. A fourth copy lived here.
|
|
147
112
|
|
|
148
|
-
|
|
113
|
+
The one thing that is this workflow's own decision: which operations exist.
|
|
114
|
+
A CRUD domain is create / get_all / get_by_id / update / delete, one file each,
|
|
115
|
+
each declared in the plan before any of them is written.
|
|
149
116
|
|
|
150
117
|
### 5. Verify
|
|
151
118
|
|
|
@@ -160,13 +127,14 @@ Check that:
|
|
|
160
127
|
- `GET /system/lint` has no warnings and no `UNTYPED_PAYLOAD` for your events
|
|
161
128
|
- `GET /system/events/schemas` lists every event the plan declared
|
|
162
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
|
|
163
131
|
|
|
164
132
|
### 6. Generate tests
|
|
165
133
|
|
|
166
134
|
Create `tests/test_{name}_plugin.py` with one test per plugin. Mock exactly
|
|
167
|
-
the tools the plan's `
|
|
135
|
+
the tools the plan's `tools:` field lists; run the rest as real in-memory
|
|
168
136
|
instances (`INSTRUCTIONS_FOR_AI.md` § Testing). Example with everything
|
|
169
|
-
mocked (`
|
|
137
|
+
mocked (`tools: [http, db, event_bus, logger]`):
|
|
170
138
|
|
|
171
139
|
```python
|
|
172
140
|
import pytest
|
|
@@ -4,21 +4,79 @@ This file is the single, absolute entry point for any AI agent (Gemini, Claude,
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## 🚦 Start here: `microcoreos status`
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
One command, before anything else. It answers the three questions that
|
|
10
|
+
silently derail a session: **which plan is actually active** (and whether it is
|
|
11
|
+
still the shipped template), **how much of it is done**, and **whether
|
|
12
|
+
`AI_CONTEXT.md` still describes the code on disk**.
|
|
13
|
+
|
|
14
|
+
There is exactly one plan path: **`plans/active_plan.yaml`**. The workflows,
|
|
15
|
+
the executor prompts and the checklist cross-check read that path and no other.
|
|
16
|
+
A plan written to `plans/my_feature.yaml` is a plan nothing will ever execute —
|
|
17
|
+
and because the shipped template is itself a valid plan, an agent that opens
|
|
18
|
+
`active_plan.yaml` and finds it untouched will build the *example* domain and
|
|
19
|
+
report success. That is why the template carries `template: true` and fails
|
|
20
|
+
validation until you delete the line.
|
|
14
21
|
|
|
15
22
|
---
|
|
16
23
|
|
|
17
|
-
##
|
|
24
|
+
## 📖 Your reading route — find your role, read those files, stop
|
|
25
|
+
|
|
26
|
+
Four roles do the work, and **each needs a different slice**. Reading outside
|
|
27
|
+
your slice is not thoroughness, it is budget: the Planner that also loads the
|
|
28
|
+
plugin templates spends ~3,000 tokens on code it will never write.
|
|
29
|
+
|
|
30
|
+
| You are… | Read exactly this | **Write exactly this** | Not this |
|
|
31
|
+
|---|---|---|---|
|
|
32
|
+
| **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
|
+
| **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
|
+
| **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
|
+
| **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 |
|
|
36
|
+
|
|
37
|
+
If you are the Planner, the first thing you write is `plans/active_plan.yaml`
|
|
38
|
+
itself — not a draft under another name that someone copies later. The copy
|
|
39
|
+
step is where the plan gets lost: a validated plan sat in `plans/twitter_plan.yaml`
|
|
40
|
+
while two sessions in a row built the template's example domain instead.
|
|
41
|
+
|
|
42
|
+
And write it **before you ask anything**. Planning is usually a conversation,
|
|
43
|
+
and checking in is welcome — but never *instead of* writing. A plan that ends
|
|
44
|
+
as prose in your reply and a *"shall I proceed?"* produced nothing: the next
|
|
45
|
+
phase reads files, not answers, and the session may not be interactive at all.
|
|
46
|
+
Write the YAML, run `microcoreos plan validate`, and then ask — the operator
|
|
47
|
+
reviews a real file rather than a description of one.
|
|
18
48
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
49
|
+
**And the commands your role may run — nothing else boots the system:**
|
|
50
|
+
|
|
51
|
+
| Role | May run |
|
|
52
|
+
|---|---|
|
|
53
|
+
| Planner | `microcoreos status`, `microcoreos plan validate` |
|
|
54
|
+
| Phase 0 Builder | `microcoreos migrate`, `microcoreos schema` |
|
|
55
|
+
| Executor | none — write your two files and stop |
|
|
56
|
+
| Coordinator | `uv run -m pytest`, `microcoreos status`, and `microcoreos` (the real boot) for the final lint |
|
|
57
|
+
|
|
58
|
+
**Never `microcoreos run` / `uv run main.py` outside that last row.** It serves
|
|
59
|
+
forever: in the foreground it hangs your session, in the background it leaves a
|
|
60
|
+
process holding the port that makes the next `microcoreos migrate` refuse to
|
|
61
|
+
run. `migrate` is the boot that ends; `status` and `schema` answer everything
|
|
62
|
+
you would have booted to find out.
|
|
63
|
+
|
|
64
|
+
**No `AI_CONTEXT.md` in the project?** It is generated, not shipped — a freshly
|
|
65
|
+
scaffolded project has none until something boots. Run `microcoreos migrate`
|
|
66
|
+
once and it appears. Do not go exploring `domains/` and `tools/` to reconstruct
|
|
67
|
+
what it would have said: that is the search the manifest exists to replace, and
|
|
68
|
+
it costs an order of magnitude more to arrive at less.
|
|
69
|
+
|
|
70
|
+
Read on demand, never up front: `INSTRUCTIONS_FOR_AI.md` (building tools,
|
|
71
|
+
testing in depth, kernel internals) · `docs/internal/TECH_DEBT.md` (only when scoping
|
|
72
|
+
work that may overlap an open item) · `domains/{domain}/models/{name}.py` (its
|
|
73
|
+
fields are already in `AI_CONTEXT.md`).
|
|
74
|
+
|
|
75
|
+
**Size the plan to the request** — over-planning a small one is a failure mode.
|
|
76
|
+
Every workflow below opens with the **same** two-file route as the Planner row
|
|
77
|
+
above, so following the ladder and following the table land in the same place;
|
|
78
|
+
each then adds only what is specific to its size. The phases themselves are in
|
|
79
|
+
`docs/PARALLEL_DEVELOPMENT.md` and are not repeated in any of them.
|
|
22
80
|
|
|
23
81
|
| Request | Workflow | Expected plan size |
|
|
24
82
|
|---|---|---|
|
|
@@ -42,6 +100,21 @@ uv run -m pytest tests/test_file.py # Run a single test
|
|
|
42
100
|
docker compose -f dev_infra/docker-compose.yml up -d # Start dev infrastructure (PostgreSQL)
|
|
43
101
|
```
|
|
44
102
|
|
|
103
|
+
The plan pipeline — prefix with `uv run` if the package is installed in a venv:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
microcoreos status # Active plan, progress, manifest freshness
|
|
107
|
+
microcoreos plan validate # The 18 plan rules, OFFLINE (no server, no jq, no curl)
|
|
108
|
+
microcoreos migrate # Apply migrations AND regenerate AI_CONTEXT.md
|
|
109
|
+
microcoreos schema # The live tables and columns, read by the db tool itself
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`microcoreos schema` is how you verify a migration. Do not reach for `sqlite3`
|
|
113
|
+
(not installed) or `import aiosqlite` from the system interpreter (it lives in
|
|
114
|
+
`.venv`) — and do not read the DB file directly: `describe_schema()` normalizes
|
|
115
|
+
types to the closed vocabulary every engine shares, so it reports what a swap
|
|
116
|
+
must preserve.
|
|
117
|
+
|
|
45
118
|
---
|
|
46
119
|
|
|
47
120
|
## 🛡️ Non-Negotiable Rules
|
|
@@ -81,63 +154,29 @@ The Kernel (ToolProxy & Container) is infrastructure-blind:
|
|
|
81
154
|
|
|
82
155
|
---
|
|
83
156
|
|
|
84
|
-
## 🔄
|
|
85
|
-
|
|
86
|
-
The canonical methodology (and its phase numbering) is `docs/PARALLEL_DEVELOPMENT.md`.
|
|
87
|
-
This is the coordinator's operational summary:
|
|
157
|
+
## 🔄 The pipeline, in five lines
|
|
88
158
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
* For passing plugins: Mark their checkbox as `[x]` in `plans/active_plan.md`.
|
|
95
|
-
* For failing plugins: **Delete** the created plugin and unit test files, keep their checkbox as `[ ]`, and spawn a new wave of clean agents to rewrite them from scratch.
|
|
96
|
-
* Repeat until all checkboxes are `[x]`.
|
|
159
|
+
The methodology, the phase numbering and the executor-prompt mechanics live in
|
|
160
|
+
`docs/PARALLEL_DEVELOPMENT.md` — **canonically, and only there**. This used to
|
|
161
|
+
be a second copy of it, which is precisely how the copy drifted: it prescribed
|
|
162
|
+
`--boot-tool db` for phase 0 long after that stopped regenerating the manifest,
|
|
163
|
+
and no one compares two documents to notice.
|
|
97
164
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
class CreateThingResponse(BaseModel):
|
|
115
|
-
success: bool
|
|
116
|
-
data: Optional[ThingData] = None
|
|
117
|
-
error: Optional[str] = None
|
|
118
|
-
|
|
119
|
-
class CreateThingPlugin(BasePlugin):
|
|
120
|
-
def __init__(self, http, db, logger):
|
|
121
|
-
self.http = http
|
|
122
|
-
self.db = db
|
|
123
|
-
self.logger = logger
|
|
124
|
-
|
|
125
|
-
async def on_boot(self):
|
|
126
|
-
self.http.add_endpoint("/things", "POST", self.execute,
|
|
127
|
-
tags=["Things"], request_model=CreateThingRequest,
|
|
128
|
-
response_model=CreateThingResponse)
|
|
129
|
-
|
|
130
|
-
async def execute(self, data: dict, context=None):
|
|
131
|
-
try:
|
|
132
|
-
req = CreateThingRequest(**data)
|
|
133
|
-
new_id = await self.db.execute(
|
|
134
|
-
"INSERT INTO things (name) VALUES ($1) RETURNING id", [req.name]
|
|
135
|
-
)
|
|
136
|
-
return {"success": True, "data": {"id": new_id, "name": req.name}}
|
|
137
|
-
except Exception as e:
|
|
138
|
-
self.logger.error(f"Failed to create thing: {e}")
|
|
139
|
-
return {"success": False, "error": "Database error"}
|
|
140
|
-
```
|
|
165
|
+
| Phase | Command | Gate |
|
|
166
|
+
|---|---|---|
|
|
167
|
+
| 1 — Plan | *(the Planner writes `plans/active_plan.yaml` + `.md`)* | `microcoreos plan validate` → **zero errors** |
|
|
168
|
+
| 0 — Foundation | *(migrations + models, 1:1 from `phase_0`)* | `microcoreos migrate` then `microcoreos schema` |
|
|
169
|
+
| 2 — Wave | *(N executors, one plugin + one test each)* | every declared file exists |
|
|
170
|
+
| 3 — Verify | `uv run -m pytest` | green, then `GET /system/lint` clean |
|
|
171
|
+
| — Reconstruct | delete the failures, respawn fresh executors | all `[x]` in `plans/active_plan.md` |
|
|
172
|
+
|
|
173
|
+
Two rules the phases do not state on their own:
|
|
174
|
+
|
|
175
|
+
- **An invalid plan is fixed in the plan, never patched in code.** Errors carry
|
|
176
|
+
the YAML that fixes them — paste it, do not re-derive it.
|
|
177
|
+
- **A plan is only ever `plans/active_plan.yaml`.** Any other filename is a plan
|
|
178
|
+
nothing will execute, and the shipped template is itself valid, so nothing but
|
|
179
|
+
its `template: true` marker can tell it apart from yours.
|
|
141
180
|
|
|
142
181
|
---
|
|
143
182
|
|
|
@@ -293,13 +293,10 @@ Async SQLite Persistence Tool (sqlite):
|
|
|
293
293
|
- **res**: EventSchemasData(schemas: dict)
|
|
294
294
|
- `GET /system/lint`
|
|
295
295
|
- **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])
|
|
296
|
-
- `POST /system/plan/validate`
|
|
297
|
-
- **req**: plan: Optional[dict], plan_yaml: Optional[str]
|
|
298
|
-
- **res**: ValidatePlanData(valid: bool, errors: list[PlanViolation(rule: int, severity: str, where: str, detail: str)], warnings: list[PlanViolation(rule: int, severity: str, where: str, detail: str)])
|
|
299
296
|
- **Events emitted**: none
|
|
300
297
|
- **Events consumed**: none
|
|
301
298
|
- **Dependencies**: container, http, logger
|
|
302
|
-
- **Plugins**: devtools.DiscoveryNamingLinterPlugin, devtools.DomainIsolationLinterPlugin, devtools.EventContractLinterPlugin, devtools.EventSchemasPlugin, devtools.FieldDivergenceLinterPlugin, devtools.
|
|
299
|
+
- **Plugins**: devtools.DiscoveryNamingLinterPlugin, devtools.DomainIsolationLinterPlugin, devtools.EventContractLinterPlugin, devtools.EventSchemasPlugin, devtools.FieldDivergenceLinterPlugin, devtools.RouteCollisionLinterPlugin, devtools.TableOwnershipLinterPlugin, devtools.ToolDocDriftLinterPlugin
|
|
303
300
|
|
|
304
301
|
### `system`
|
|
305
302
|
- **Tables**: none
|
|
@@ -344,8 +341,10 @@ Your task line names either a **feature** or a **flow's tests**:
|
|
|
344
341
|
- Flow-tests task → 1. the flow's `e2e_test` (trigger the happy path, assert
|
|
345
342
|
the causal chain with `tests/helpers/trace_chains.py`:
|
|
346
343
|
`assert_chain(build_tree(bus.get_trace_history()), [...])`) and 2. its
|
|
347
|
-
`sad_path_test` (force the consumer to fail
|
|
348
|
-
|
|
344
|
+
`sad_path_test` (force the consumer to fail — the mock must raise on the
|
|
345
|
+
FIRST tool call the handler makes, since an idempotency guard runs before
|
|
346
|
+
the effect — and assert `_dlq.<event>` appears as a child of the failed
|
|
347
|
+
event in the same tree).
|
|
349
348
|
|
|
350
349
|
Nothing else: no migrations, no entity models, no edits to `main.py`, no
|
|
351
350
|
touching other domains or other tasks' files. When both files are written,
|
|
@@ -357,8 +356,11 @@ follow-ups.
|
|
|
357
356
|
1. **Schemas inline** — request, response AND event payload models at the top
|
|
358
357
|
of the plugin file. Never import them from `models/` or other domains.
|
|
359
358
|
2. **DI by parameter name** — `__init__(self, http, db, logger)` receives the
|
|
360
|
-
tools named `http`, `db`, `logger`.
|
|
361
|
-
|
|
359
|
+
tools named `http`, `db`, `logger`. No hardcoded imports from `tools/`.
|
|
360
|
+
**Your parameters are exactly your feature's `tools:` list in the plan, in
|
|
361
|
+
that order** — not what a template happens to show. The plan is the
|
|
362
|
+
contract, and the test is written against that same list: add a `logger`
|
|
363
|
+
nobody declared and the two no longer fit.
|
|
362
364
|
3. **Return envelope** — `{"success": bool, "data": ..., "error": ...}`:
|
|
363
365
|
`success` always present, `data` on success, `error` on failure. Responses
|
|
364
366
|
serialize AS-IS — `response_model` does NOT backfill omitted keys, so an
|
|
@@ -398,6 +400,26 @@ follow-ups.
|
|
|
398
400
|
coherent without any of you coordinating: your feature is written in
|
|
399
401
|
isolation, but its vocabulary is shared.
|
|
400
402
|
|
|
403
|
+
11. **Idempotent consumer** — when the plan's flow link says
|
|
404
|
+
`idempotent: true`, guard on `event.id` (the envelope is frozen, so a
|
|
405
|
+
redelivery carries the same one) and mark it **after** the effect:
|
|
406
|
+
|
|
407
|
+
```python
|
|
408
|
+
if await self.state.has(event.id, namespace="thing-seen"):
|
|
409
|
+
return # duplicate: already applied
|
|
410
|
+
await self.state.increment(...) # the effect
|
|
411
|
+
await self.state.set(event.id, True, namespace="thing-seen", ttl=3600)
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
Marking first drops the event: the effect raises, the retry hits the guard,
|
|
415
|
+
returns "already seen" — no work, no error, no DLQ. Write the
|
|
416
|
+
double-delivery test under the exact name `idempotency_test` gives, with a
|
|
417
|
+
real `StateTool()`: an `AsyncMock` returns truthy from `has()`, so the guard
|
|
418
|
+
swallows everything and the test proves nothing. Rule 6 says a *plugin*
|
|
419
|
+
never imports the envelope; a *test* has to build one —
|
|
420
|
+
`from tools.event_bus.envelope import EventEnvelope` — and deliver the same
|
|
421
|
+
instance twice.
|
|
422
|
+
|
|
401
423
|
### Templates — one per deliverable type, copy the one your task matches
|
|
402
424
|
|
|
403
425
|
Each is a whole file, imports to last line; nothing a feature or flow-tests
|
|
@@ -600,7 +622,8 @@ x = tool.method.return_value.__aenter__.return_value # bound by `as x:`
|
|
|
600
622
|
PLAN (route, envelope shape, declared tables, declared payload keys) —
|
|
601
623
|
never from your own implementation. The test is the contract's proof; a
|
|
602
624
|
test that mirrors the code proves nothing.
|
|
603
|
-
- Mock exactly the tools your feature's `
|
|
625
|
+
- Mock exactly the tools your feature's `tools:` lists — all of them; an
|
|
626
|
+
omitted `logger` is still a required positional argument
|
|
604
627
|
(`unittest.mock.AsyncMock` / `MagicMock`); run every other injected tool as
|
|
605
628
|
a real in-memory instance (SQLite `:memory:` with your domain's migration
|
|
606
629
|
applied, in-process event bus).
|