wicked-garden 12.27.0
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.
- package/.claude-plugin/archetypes.json +166 -0
- package/.claude-plugin/components.json +75 -0
- package/.claude-plugin/marketplace.json +15 -0
- package/.claude-plugin/plugin.json +174 -0
- package/.claude-plugin/specialist.json +48 -0
- package/.claude-plugin/stack-registry.json +96 -0
- package/AGENTS.md +13 -0
- package/CHANGELOG.md +712 -0
- package/CONTRIBUTING.md +99 -0
- package/ETHOS.md +83 -0
- package/LICENSE +21 -0
- package/README.md +115 -0
- package/WICKED_GARDEN_BUS_EVENTS.md +149 -0
- package/agents/agentic/architect.md +449 -0
- package/agents/agentic/performance-analyst.md +702 -0
- package/agents/agentic/safety-reviewer.md +662 -0
- package/agents/crew/implementer.md +163 -0
- package/agents/crew/researcher.md +94 -0
- package/agents/crew/reviewer.md +184 -0
- package/agents/data/data-engineer.md +227 -0
- package/agents/engineering/api-documentarian.md +440 -0
- package/agents/engineering/migration-engineer.md +301 -0
- package/agents/engineering/solution-architect.md +208 -0
- package/agents/jam/brainstorm-facilitator.md +297 -0
- package/agents/jam/council.md +322 -0
- package/agents/jam/quick-facilitator.md +85 -0
- package/agents/persona/persona-agent.md +121 -0
- package/agents/platform/compliance-officer.md +266 -0
- package/agents/platform/privacy-expert.md +496 -0
- package/agents/platform/security-engineer.md +287 -0
- package/agents/product/a11y-expert.md +323 -0
- package/agents/product/requirements-analyst.md +238 -0
- package/agents/product/ui-reviewer.md +180 -0
- package/agents/product/ux-designer.md +199 -0
- package/agents/product/value-strategist.md +269 -0
- package/agents/qe/semantic-reviewer.md +78 -0
- package/commands/agentic/audit.md +24 -0
- package/commands/agentic/design.md +24 -0
- package/commands/agentic/frameworks.md +25 -0
- package/commands/agentic/review.md +27 -0
- package/commands/archetype/build.md +12 -0
- package/commands/archetype/decide.md +12 -0
- package/commands/archetype/explore.md +12 -0
- package/commands/archetype/incident.md +12 -0
- package/commands/archetype/migrate.md +12 -0
- package/commands/archetype/modernize.md +12 -0
- package/commands/archetype/review.md +14 -0
- package/commands/archetype/ship.md +12 -0
- package/commands/archetype/specify.md +12 -0
- package/commands/archetype/triage.md +12 -0
- package/commands/compile.md +37 -0
- package/commands/data/analyze.md +18 -0
- package/commands/data/data.md +21 -0
- package/commands/data/ml.md +18 -0
- package/commands/data/ontology.md +22 -0
- package/commands/data/pipeline.md +17 -0
- package/commands/deliberate.md +181 -0
- package/commands/engineering/add-field.md +77 -0
- package/commands/engineering/apply.md +58 -0
- package/commands/engineering/arch.md +26 -0
- package/commands/engineering/debug.md +27 -0
- package/commands/engineering/docs.md +27 -0
- package/commands/engineering/new-generator.md +257 -0
- package/commands/engineering/patch-plan.md +59 -0
- package/commands/engineering/plan.md +29 -0
- package/commands/engineering/remove.md +48 -0
- package/commands/engineering/rename.md +44 -0
- package/commands/engineering/review.md +32 -0
- package/commands/help.md +128 -0
- package/commands/install.md +118 -0
- package/commands/intent.md +54 -0
- package/commands/jam/brainstorm.md +37 -0
- package/commands/jam/council.md +49 -0
- package/commands/jam/quick.md +21 -0
- package/commands/jam/revisit.md +79 -0
- package/commands/persona/as.md +136 -0
- package/commands/persona/define.md +24 -0
- package/commands/persona/list.md +118 -0
- package/commands/platform/actions.md +20 -0
- package/commands/platform/assert.md +18 -0
- package/commands/platform/audit.md +23 -0
- package/commands/platform/compliance.md +20 -0
- package/commands/platform/gh.md +22 -0
- package/commands/platform/health.md +22 -0
- package/commands/platform/incident.md +23 -0
- package/commands/platform/infra.md +24 -0
- package/commands/platform/peer-health.md +103 -0
- package/commands/platform/plugin-health.md +58 -0
- package/commands/platform/security.md +38 -0
- package/commands/platform/toolchain.md +96 -0
- package/commands/platform/traces.md +20 -0
- package/commands/product/a11y.md +19 -0
- package/commands/product/acceptance.md +19 -0
- package/commands/product/align.md +20 -0
- package/commands/product/analyze.md +23 -0
- package/commands/product/elicit.md +19 -0
- package/commands/product/listen.md +24 -0
- package/commands/product/mockup.md +20 -0
- package/commands/product/screenshot.md +19 -0
- package/commands/product/strategy.md +40 -0
- package/commands/product/synthesize.md +23 -0
- package/commands/product/ux-review.md +42 -0
- package/commands/product/ux.md +20 -0
- package/commands/product/visual-direction.md +34 -0
- package/commands/prove.md +47 -0
- package/commands/report-issue.md +243 -0
- package/commands/reset.md +144 -0
- package/commands/search/blast-radius.md +58 -0
- package/commands/search/hotspots.md +55 -0
- package/commands/search/index.md +38 -0
- package/commands/search/lineage.md +47 -0
- package/commands/search/service-map.md +79 -0
- package/commands/setup.md +363 -0
- package/commands/smaht/briefing.md +200 -0
- package/commands/smaht/events-import.md +95 -0
- package/commands/smaht/state.md +19 -0
- package/commands/where-am-i.md +58 -0
- package/docs/README.md +32 -0
- package/docs/adr/0001-code-relationship-graph-engine.md +116 -0
- package/docs/adr/0002-major-cleanup-keep-cut.md +91 -0
- package/docs/adr/0003-peers-gate-required-layers-optin.md +41 -0
- package/docs/adr/0004-code-graph-moves-to-wicked-brain.md +47 -0
- package/docs/brain-chunk-format.md +135 -0
- package/docs/compiler.md +95 -0
- package/docs/daemon-api.md +70 -0
- package/docs/domains.md +197 -0
- package/docs/getting-started.md +208 -0
- package/docs/plans/2026-06-08-wicked-loom-conduct.md +1337 -0
- package/docs/plans/2026-06-08-wicked-loom-contract.md +638 -0
- package/docs/plans/2026-06-08-wicked-loom-cutover.md +1300 -0
- package/docs/plans/2026-06-08-wicked-loom-expand-compose.md +843 -0
- package/docs/plans/2026-06-08-wicked-loom-flow-cutover.md +403 -0
- package/docs/plans/2026-06-10-codegraph-to-brain-phase1a-graph-core.md +838 -0
- package/docs/plans/2026-06-10-codegraph-to-brain-phase1b-injected-edges.md +44 -0
- package/docs/required-peers.md +60 -0
- package/docs/specs/2026-06-08-wicked-loom-extraction.md +225 -0
- package/docs/specs/2026-06-10-codegraph-to-brain-migration.md +168 -0
- package/docs/v11/archetypes.md +231 -0
- package/hooks/hooks.json +275 -0
- package/hooks/scripts/bootstrap.py +1797 -0
- package/hooks/scripts/invoke.py +62 -0
- package/hooks/scripts/notification.py +166 -0
- package/hooks/scripts/permission_request.py +223 -0
- package/hooks/scripts/post_tool.py +1374 -0
- package/hooks/scripts/pre_compact.py +272 -0
- package/hooks/scripts/pre_tool.py +1278 -0
- package/hooks/scripts/prompt_submit.py +1109 -0
- package/hooks/scripts/session_end.py +95 -0
- package/hooks/scripts/stop.py +624 -0
- package/hooks/scripts/subagent_lifecycle.py +464 -0
- package/hooks/scripts/subscribers/on_gate_decided.py +84 -0
- package/hooks/scripts/task_completed.py +322 -0
- package/hooks/scripts/worktree_guard.py +148 -0
- package/hooks/subscriptions/on_gate_decided.json +9 -0
- package/install.mjs +128 -0
- package/package.json +41 -0
- package/pyproject.toml +59 -0
- package/schemas/README.md +32 -0
- package/schemas/assert_contracts.json +37 -0
- package/schemas/health_probe.json +46 -0
- package/schemas/plugin_status.json +37 -0
- package/scripts/_adapters/__init__.py +166 -0
- package/scripts/_adapters/jira_adapter.py +18 -0
- package/scripts/_adapters/linear_adapter.py +18 -0
- package/scripts/_adapters/miro_adapter.py +18 -0
- package/scripts/_adapters/notion_adapter.py +18 -0
- package/scripts/_agents.py +357 -0
- package/scripts/_brain_ingest/__init__.py +2 -0
- package/scripts/_brain_ingest/session_fact_extractor.py +341 -0
- package/scripts/_brain_port.py +163 -0
- package/scripts/_bus.py +1081 -0
- package/scripts/_bus_catalog_gen.py +93 -0
- package/scripts/_bus_consumers.json +46 -0
- package/scripts/_bus_emit.py +43 -0
- package/scripts/_capability_registry.py +334 -0
- package/scripts/_capability_resolver.py +322 -0
- package/scripts/_domain_store.py +559 -0
- package/scripts/_event_log_reader.py +154 -0
- package/scripts/_event_schema.py +305 -0
- package/scripts/_event_store.py +397 -0
- package/scripts/_heavy_cadence.py +324 -0
- package/scripts/_integration_resolver.py +321 -0
- package/scripts/_logger.py +151 -0
- package/scripts/_loom.py +482 -0
- package/scripts/_paths.py +133 -0
- package/scripts/_python.sh +22 -0
- package/scripts/_run.py +137 -0
- package/scripts/_session.py +502 -0
- package/scripts/_validate_registry.py +1042 -0
- package/scripts/_wicked_testing_probe.py +277 -0
- package/scripts/agentic/analyze_agents.py +391 -0
- package/scripts/agentic/detect_framework.py +420 -0
- package/scripts/agentic/issue_taxonomy.py +357 -0
- package/scripts/agentic/pattern_scorer.py +434 -0
- package/scripts/classify/persist.py +169 -0
- package/scripts/compiler/compile.py +552 -0
- package/scripts/compiler/phase0/VERDICT.md +60 -0
- package/scripts/compiler/phase0/detect.py +292 -0
- package/scripts/compiler/phase0/emit.py +228 -0
- package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.bindings.json +30 -0
- package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.lint-output.txt +17 -0
- package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/.wg/evidence/failing-tests.txt +4 -0
- package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/.wg/evidence/honest-tests.txt +2 -0
- package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/fab-fail.build.md +8 -0
- package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/fab-missing.build.md +8 -0
- package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/honest.build.md +8 -0
- package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/wg_check_claims.py +174 -0
- package/scripts/compiler/phase0/evidence/command_iq.bindings.json +45 -0
- package/scripts/compiler/phase0/evidence/command_iq.lint-output.txt +17 -0
- package/scripts/compiler/phase0/evidence/command_iq.stage/.projects/evidence-domain/phase-1-clarify/evidence/failing-tests.txt +4 -0
- package/scripts/compiler/phase0/evidence/command_iq.stage/.projects/evidence-domain/phase-1-clarify/evidence/honest-tests.txt +3 -0
- package/scripts/compiler/phase0/evidence/command_iq.stage/fab-fail.build.md +8 -0
- package/scripts/compiler/phase0/evidence/command_iq.stage/fab-missing.build.md +8 -0
- package/scripts/compiler/phase0/evidence/command_iq.stage/honest.build.md +8 -0
- package/scripts/compiler/phase0/evidence/command_iq.stage/wg_check_claims.py +174 -0
- package/scripts/compiler/phase0/evidence/memos.bindings.json +28 -0
- package/scripts/compiler/phase0/evidence/memos.lint-output.txt +17 -0
- package/scripts/compiler/phase0/evidence/memos.stage/.wg/evidence/failing-tests.txt +4 -0
- package/scripts/compiler/phase0/evidence/memos.stage/.wg/evidence/honest-tests.txt +2 -0
- package/scripts/compiler/phase0/evidence/memos.stage/fab-fail.build.md +8 -0
- package/scripts/compiler/phase0/evidence/memos.stage/fab-missing.build.md +8 -0
- package/scripts/compiler/phase0/evidence/memos.stage/honest.build.md +8 -0
- package/scripts/compiler/phase0/evidence/memos.stage/wg_check_claims.py +174 -0
- package/scripts/compiler/phase0/evidence/verdict.json +104 -0
- package/scripts/compiler/phase0/evidence/wicked-bus.bindings.json +29 -0
- package/scripts/compiler/phase0/evidence/wicked-bus.lint-output.txt +17 -0
- package/scripts/compiler/phase0/evidence/wicked-bus.stage/coverage/failing-tests.txt +4 -0
- package/scripts/compiler/phase0/evidence/wicked-bus.stage/coverage/honest-tests.txt +3 -0
- package/scripts/compiler/phase0/evidence/wicked-bus.stage/fab-fail.build.md +8 -0
- package/scripts/compiler/phase0/evidence/wicked-bus.stage/fab-missing.build.md +8 -0
- package/scripts/compiler/phase0/evidence/wicked-bus.stage/honest.build.md +8 -0
- package/scripts/compiler/phase0/evidence/wicked-bus.stage/wg_check_claims.py +174 -0
- package/scripts/compiler/phase0/evidence/wire-vault-transcript.txt +60 -0
- package/scripts/compiler/phase0/run_probe.py +234 -0
- package/scripts/compiler/phase0/wire_vault.py +310 -0
- package/scripts/crew/__init__.py +0 -0
- package/scripts/crew/_task_audit_writer.py +243 -0
- package/scripts/crew/_task_reader.py +389 -0
- package/scripts/crew/archetypes_v11.py +348 -0
- package/scripts/crew/detectors/__init__.py +12 -0
- package/scripts/crew/flow_compiler.py +121 -0
- package/scripts/crew/phase_manager.py +703 -0
- package/scripts/crew/scope_delta.py +264 -0
- package/scripts/crew/specialist_resolver.py +218 -0
- package/scripts/crew/stack_registry.py +189 -0
- package/scripts/data/data_profiler.py +271 -0
- package/scripts/data/ontology_recommender.py +231 -0
- package/scripts/data/schema_validator.py +267 -0
- package/scripts/delivery/drift.py +739 -0
- package/scripts/delivery/telemetry.py +614 -0
- package/scripts/engineering/patch/codegraph_db.py +125 -0
- package/scripts/engineering/patch/generators/__init__.py +56 -0
- package/scripts/engineering/patch/generators/base.py +373 -0
- package/scripts/engineering/patch/generators/csharp_generator.py +494 -0
- package/scripts/engineering/patch/generators/go_generator.py +430 -0
- package/scripts/engineering/patch/generators/java_generator.py +928 -0
- package/scripts/engineering/patch/generators/jsp_generator.py +454 -0
- package/scripts/engineering/patch/generators/kotlin_generator.py +561 -0
- package/scripts/engineering/patch/generators/perl_generator.py +510 -0
- package/scripts/engineering/patch/generators/php_generator.py +644 -0
- package/scripts/engineering/patch/generators/propagation_engine.py +929 -0
- package/scripts/engineering/patch/generators/python_generator.py +487 -0
- package/scripts/engineering/patch/generators/ruby_generator.py +463 -0
- package/scripts/engineering/patch/generators/rust_generator.py +400 -0
- package/scripts/engineering/patch/generators/sql_generator.py +594 -0
- package/scripts/engineering/patch/generators/typescript_generator.py +439 -0
- package/scripts/engineering/patch/patch.py +800 -0
- package/scripts/engineering/patch/safety.py +429 -0
- package/scripts/jam/__init__.py +0 -0
- package/scripts/jam/_bus_consumers.py +271 -0
- package/scripts/jam/agentic_cli_registry.py +703 -0
- package/scripts/jam/consensus.py +737 -0
- package/scripts/jam/detect_clis.py +409 -0
- package/scripts/jam/jam.py +283 -0
- package/scripts/jam/save_transcript.py +171 -0
- package/scripts/loom/__init__.py +22 -0
- package/scripts/loom/cli.py +170 -0
- package/scripts/loom/compose.py +180 -0
- package/scripts/loom/gate.py +120 -0
- package/scripts/loom/manifest.py +140 -0
- package/scripts/loom/resolve.py +59 -0
- package/scripts/persona/__init__.py +0 -0
- package/scripts/persona/registry.py +688 -0
- package/scripts/platform/guard_pipeline.py +1214 -0
- package/scripts/platform/guard_profiles.py +216 -0
- package/scripts/platform/observability/assert_contracts.py +434 -0
- package/scripts/platform/observability/health_probe.py +915 -0
- package/scripts/platform/observability/ops_log_viewer.py +209 -0
- package/scripts/platform/prereq_doctor.py +567 -0
- package/scripts/qe/conditions_manifest.py +260 -0
- package/scripts/qe/content_sanitizer.py +160 -0
- package/scripts/qe/coverage_tracker.py +535 -0
- package/scripts/qe/discover_scenarios.py +243 -0
- package/scripts/qe/evidence_tracker.py +232 -0
- package/scripts/qe/prove.py +196 -0
- package/scripts/qe/registry_lookup.py +80 -0
- package/scripts/qe/registry_store.py +176 -0
- package/scripts/qe/semantic_review.py +1005 -0
- package/scripts/qe/vault_gate.py +382 -0
- package/scripts/qe/verdict_audit.py +175 -0
- package/scripts/qe/verdict_schema.py +335 -0
- package/scripts/reset.py +220 -0
- package/scripts/resolve_path.py +22 -0
- package/scripts/sentinel/invariants.py +402 -0
- package/scripts/sentinel/pre_push.py +73 -0
- package/scripts/setup/__init__.py +1 -0
- package/scripts/setup/detect_state.py +114 -0
- package/scripts/setup/migrate_legacy.py +67 -0
- package/scripts/setup/migrate_v6_projects.py +232 -0
- package/scripts/setup/onboarding.py +114 -0
- package/scripts/smaht/_bus_consumers.py +98 -0
- package/scripts/smaht/adapters/__init__.py +83 -0
- package/scripts/smaht/adapters/brain_adapter.py +254 -0
- package/scripts/smaht/adapters/context7_adapter.py +438 -0
- package/scripts/smaht/adapters/delegation_adapter.py +104 -0
- package/scripts/smaht/adapters/domain_adapter.py +206 -0
- package/scripts/smaht/adapters/events_adapter.py +231 -0
- package/scripts/smaht/adapters/tools_adapter.py +49 -0
- package/scripts/smaht/cheatsheet_store.py +236 -0
- package/scripts/smaht/context_package.py +386 -0
- package/scripts/smaht/knowledge_graph.py +395 -0
- package/scripts/smaht/propose_skills.py +856 -0
- package/scripts/smaht/test_cheatsheet_store.py +428 -0
- package/scripts/statusline.py +151 -0
- package/scripts/where_am_i.py +236 -0
- package/skills/agentic/agentic-patterns/SKILL.md +91 -0
- package/skills/agentic/agentic-patterns/refs/anti-patterns-design.md +283 -0
- package/skills/agentic/agentic-patterns/refs/anti-patterns-operational.md +216 -0
- package/skills/agentic/agentic-patterns/refs/pattern-catalog.md +329 -0
- package/skills/agentic/context-engineering/SKILL.md +142 -0
- package/skills/agentic/context-engineering/refs/caching-and-optimization.md +251 -0
- package/skills/agentic/context-engineering/refs/compression-techniques.md +171 -0
- package/skills/agentic/context-engineering/refs/cost-calculation-budget.md +250 -0
- package/skills/agentic/context-engineering/refs/cost-optimization-reporting.md +300 -0
- package/skills/agentic/context-engineering/refs/selective-loading.md +179 -0
- package/skills/agentic/frameworks/SKILL.md +191 -0
- package/skills/agentic/frameworks/refs/framework-profiles-1.md +209 -0
- package/skills/agentic/frameworks/refs/framework-profiles-2.md +214 -0
- package/skills/agentic/frameworks/refs/migration-patterns-paths.md +267 -0
- package/skills/agentic/frameworks/refs/migration-patterns-testing.md +208 -0
- package/skills/agentic/refs/audit.md +134 -0
- package/skills/agentic/refs/design.md +137 -0
- package/skills/agentic/refs/frameworks.md +102 -0
- package/skills/agentic/refs/review.md +160 -0
- package/skills/agentic/review-methodology/SKILL.md +186 -0
- package/skills/agentic/review-methodology/refs/deliverable-templates.md +246 -0
- package/skills/agentic/review-methodology/refs/issue-taxonomy-quality-testing.md +277 -0
- package/skills/agentic/review-methodology/refs/issue-taxonomy-reliability-safety.md +253 -0
- package/skills/agentic/trust-and-safety/SKILL.md +177 -0
- package/skills/agentic/trust-and-safety/refs/guardrails-actions.md +246 -0
- package/skills/agentic/trust-and-safety/refs/guardrails-input-output.md +238 -0
- package/skills/agentic/trust-and-safety/refs/guardrails-resources.md +209 -0
- package/skills/agentic/trust-and-safety/refs/safety-checklist-advanced.md +209 -0
- package/skills/agentic/trust-and-safety/refs/safety-checklist-core.md +215 -0
- package/skills/archetype/SKILL.md +104 -0
- package/skills/archetype/refs/build.md +142 -0
- package/skills/archetype/refs/decide.md +117 -0
- package/skills/archetype/refs/explore.md +66 -0
- package/skills/archetype/refs/incident.md +149 -0
- package/skills/archetype/refs/migrate.md +170 -0
- package/skills/archetype/refs/modernize.md +187 -0
- package/skills/archetype/refs/review.md +145 -0
- package/skills/archetype/refs/ship.md +117 -0
- package/skills/archetype/refs/specify.md +108 -0
- package/skills/archetype/refs/triage.md +62 -0
- package/skills/classify/SKILL.md +157 -0
- package/skills/data/analysis/SKILL.md +125 -0
- package/skills/data/analysis/refs/templates.md +170 -0
- package/skills/data/data/SKILL.md +118 -0
- package/skills/data/data/refs/examples.md +235 -0
- package/skills/data/ml/SKILL.md +125 -0
- package/skills/data/ml/refs/templates.md +172 -0
- package/skills/data/pipeline/SKILL.md +136 -0
- package/skills/data/pipeline/refs/templates.md +175 -0
- package/skills/data/refs/analyze.md +123 -0
- package/skills/data/refs/data.md +95 -0
- package/skills/data/refs/ml.md +118 -0
- package/skills/data/refs/pipeline.md +92 -0
- package/skills/deliberate/SKILL.md +125 -0
- package/skills/deliberate/refs/architecture-lens.md +37 -0
- package/skills/deliberate/refs/assumptions-lens.md +43 -0
- package/skills/deliberate/refs/code-lens.md +38 -0
- package/skills/deliberate/refs/content-lens.md +37 -0
- package/skills/deliberate/refs/data-lens.md +43 -0
- package/skills/deliberate/refs/design-lens.md +37 -0
- package/skills/deliberate/refs/opportunity-patterns.md +107 -0
- package/skills/deliberate/refs/ops-lens.md +43 -0
- package/skills/deliberate/refs/product-lens.md +46 -0
- package/skills/deliberate/refs/rethink-framework.md +77 -0
- package/skills/deliberate/refs/security-lens.md +47 -0
- package/skills/deliberate/refs/testing-lens.md +45 -0
- package/skills/deliberate/refs/timing-lens.md +42 -0
- package/skills/engineering/architecture/SKILL.md +177 -0
- package/skills/engineering/architecture/refs/adr-template.md +268 -0
- package/skills/engineering/architecture/refs/architecture-template-deployment.md +176 -0
- package/skills/engineering/architecture/refs/architecture-template-design.md +300 -0
- package/skills/engineering/architecture/refs/examples-ecommerce-analytics.md +299 -0
- package/skills/engineering/architecture/refs/examples-saas-trading.md +291 -0
- package/skills/engineering/architecture/refs/patterns-advanced.md +238 -0
- package/skills/engineering/architecture/refs/patterns-structural.md +303 -0
- package/skills/engineering/backend/SKILL.md +102 -0
- package/skills/engineering/backend/refs/checklists.md +146 -0
- package/skills/engineering/backend/refs/patterns.md +181 -0
- package/skills/engineering/debugging/SKILL.md +99 -0
- package/skills/engineering/debugging/refs/patterns.md +248 -0
- package/skills/engineering/debugging/refs/process.md +203 -0
- package/skills/engineering/docs/SKILL.md +97 -0
- package/skills/engineering/engineering/SKILL.md +88 -0
- package/skills/engineering/engineering/refs/checklists.md +91 -0
- package/skills/engineering/engineering/refs/templates.md +71 -0
- package/skills/engineering/frontend/SKILL.md +92 -0
- package/skills/engineering/frontend/refs/checklists.md +96 -0
- package/skills/engineering/frontend/refs/patterns.md +167 -0
- package/skills/engineering/integration/SKILL.md +183 -0
- package/skills/engineering/integration/refs/error-handling-best-practices.md +139 -0
- package/skills/engineering/integration/refs/error-handling-error-classes-typescript.md +212 -0
- package/skills/engineering/integration/refs/error-handling-error-response-format.md +301 -0
- package/skills/engineering/integration/refs/error-handling-retry-strategies.md +190 -0
- package/skills/engineering/integration/refs/event-schemas-best-practices.md +225 -0
- package/skills/engineering/integration/refs/event-schemas-event-naming-conventions.md +303 -0
- package/skills/engineering/integration/refs/event-schemas-event-structure.md +288 -0
- package/skills/engineering/integration/refs/graphql-best-practices.md +138 -0
- package/skills/engineering/integration/refs/graphql-error-handling.md +198 -0
- package/skills/engineering/integration/refs/graphql-graphql-basics.md +197 -0
- package/skills/engineering/integration/refs/graphql-query-examples.md +226 -0
- package/skills/engineering/integration/refs/graphql-schema-design-patterns.md +280 -0
- package/skills/engineering/integration/refs/grpc-grpc-overview.md +246 -0
- package/skills/engineering/integration/refs/grpc-implementation-examples.md +269 -0
- package/skills/engineering/integration/refs/grpc-metadata-headers.md +304 -0
- package/skills/engineering/integration/refs/grpc-testing.md +49 -0
- package/skills/engineering/integration/refs/openapi-template.yaml +682 -0
- package/skills/engineering/integration/refs/resilience-best-practices.md +109 -0
- package/skills/engineering/integration/refs/resilience-circuit-breaker-pattern.md +302 -0
- package/skills/engineering/integration/refs/resilience-rate-limiting.md +245 -0
- package/skills/engineering/integration/refs/resilience-timeout-pattern.md +212 -0
- package/skills/engineering/integration/refs/rest-api-fundamentals.md +299 -0
- package/skills/engineering/integration/refs/rest-api-responses-security.md +302 -0
- package/skills/engineering/integration/refs/versioning-breaking-changes.md +237 -0
- package/skills/engineering/integration/refs/versioning-migration-strategies.md +257 -0
- package/skills/engineering/integration/refs/versioning-versioning-strategies.md +296 -0
- package/skills/engineering/large-scale-migration/SKILL.md +65 -0
- package/skills/engineering/patch/SKILL.md +184 -0
- package/skills/engineering/refs/arch.md +126 -0
- package/skills/engineering/refs/debug.md +142 -0
- package/skills/engineering/refs/docs.md +190 -0
- package/skills/engineering/refs/plan.md +109 -0
- package/skills/engineering/refs/review.md +138 -0
- package/skills/engineering/system-design/SKILL.md +154 -0
- package/skills/engineering/system-design/refs/anti-patterns-big-ball-of-mud.md +254 -0
- package/skills/engineering/system-design/refs/anti-patterns-circular-dependencies.md +241 -0
- package/skills/engineering/system-design/refs/anti-patterns-feature-envy.md +168 -0
- package/skills/engineering/system-design/refs/anti-patterns-god-object.md +247 -0
- package/skills/engineering/system-design/refs/component-template-lifecycle.md +210 -0
- package/skills/engineering/system-design/refs/component-template-maintenance.md +219 -0
- package/skills/engineering/system-design/refs/component-template-structure.md +276 -0
- package/skills/engineering/system-design/refs/dependencies-best-practices.md +260 -0
- package/skills/engineering/system-design/refs/dependencies-dependency-principles.md +295 -0
- package/skills/engineering/system-design/refs/dependencies-dependency-tracking.md +261 -0
- package/skills/engineering/system-design/refs/interface-template-lifecycle.md +211 -0
- package/skills/engineering/system-design/refs/interface-template-maintenance.md +212 -0
- package/skills/engineering/system-design/refs/interface-template-structure.md +269 -0
- package/skills/engineering/system-design/refs/patterns-dependency-injection.md +151 -0
- package/skills/engineering/system-design/refs/patterns-facade-pattern.md +257 -0
- package/skills/engineering/system-design/refs/patterns-layered-pattern.md +204 -0
- package/skills/engineering/system-design/refs/patterns-plugin-architecture.md +229 -0
- package/skills/engineering/unit-test-quality/SKILL.md +194 -0
- package/skills/ground/SKILL.md +130 -0
- package/skills/integration-discovery/SKILL.md +168 -0
- package/skills/integration-discovery/refs/capabilities.md +282 -0
- package/skills/integration-discovery/refs/cli-detection.md +295 -0
- package/skills/integration-discovery/refs/discovery-sources.md +233 -0
- package/skills/integration-discovery/refs/task-patterns.md +206 -0
- package/skills/jam/SKILL.md +45 -0
- package/skills/jam/refs/facilitation-patterns.md +125 -0
- package/skills/jam/refs/quick.md +64 -0
- package/skills/jam/refs/synthesis-patterns.md +114 -0
- package/skills/multi-model/SKILL.md +168 -0
- package/skills/multi-model/refs/aichat.md +119 -0
- package/skills/multi-model/refs/aider.md +100 -0
- package/skills/multi-model/refs/auditability.md +238 -0
- package/skills/multi-model/refs/codex.md +169 -0
- package/skills/multi-model/refs/context.md +306 -0
- package/skills/multi-model/refs/copilot.md +161 -0
- package/skills/multi-model/refs/examples.md +224 -0
- package/skills/multi-model/refs/gemini.md +185 -0
- package/skills/multi-model/refs/goose.md +112 -0
- package/skills/multi-model/refs/llm.md +124 -0
- package/skills/multi-model/refs/opencode.md +196 -0
- package/skills/multi-model/refs/orchestration.md +188 -0
- package/skills/multi-model/refs/pi.md +208 -0
- package/skills/persona/SKILL.md +144 -0
- package/skills/persona/refs/define.md +133 -0
- package/skills/platform/actions/refs/actions.md +114 -0
- package/skills/platform/audit/SKILL.md +174 -0
- package/skills/platform/audit/refs/audit.md +145 -0
- package/skills/platform/audit/refs/checklists-evidence-operations.md +144 -0
- package/skills/platform/audit/refs/checklists-gdpr-pci-evidence.md +213 -0
- package/skills/platform/audit/refs/checklists-soc2-hipaa.md +120 -0
- package/skills/platform/audit/refs/frameworks-gdpr-pci.md +225 -0
- package/skills/platform/audit/refs/frameworks-soc2-hipaa.md +228 -0
- package/skills/platform/compliance/SKILL.md +169 -0
- package/skills/platform/compliance/refs/checklists.md +303 -0
- package/skills/platform/compliance/refs/compliance.md +117 -0
- package/skills/platform/compliance/refs/frameworks.md +282 -0
- package/skills/platform/errors/SKILL.md +183 -0
- package/skills/platform/errors/refs/patterns.md +271 -0
- package/skills/platform/errors/refs/severity.md +264 -0
- package/skills/platform/gate-benchmark-rebaseline/SKILL.md +108 -0
- package/skills/platform/gh/refs/gh.md +105 -0
- package/skills/platform/gh-cli/SKILL.md +120 -0
- package/skills/platform/gh-cli/refs/automation.md +63 -0
- package/skills/platform/gh-cli/refs/gh-ops.md +103 -0
- package/skills/platform/gh-cli/refs/patterns.md +132 -0
- package/skills/platform/gh-cli/scripts/gh_ops.py +454 -0
- package/skills/platform/github-actions/SKILL.md +159 -0
- package/skills/platform/github-actions/refs/security.md +150 -0
- package/skills/platform/github-actions/refs/templates.md +230 -0
- package/skills/platform/github-actions/refs/troubleshooting.md +174 -0
- package/skills/platform/github-actions/scripts/workflow_gen.py +215 -0
- package/skills/platform/gitlab-ci/SKILL.md +140 -0
- package/skills/platform/gitlab-ci/refs/templates.md +223 -0
- package/skills/platform/gitlab-ci/refs/troubleshooting.md +213 -0
- package/skills/platform/glab-cli/SKILL.md +133 -0
- package/skills/platform/glab-cli/refs/ci-debugging.md +95 -0
- package/skills/platform/glab-cli/refs/glab-ops.md +78 -0
- package/skills/platform/glab-cli/refs/patterns.md +131 -0
- package/skills/platform/glab-cli/scripts/glab_ops.py +313 -0
- package/skills/platform/health/SKILL.md +177 -0
- package/skills/platform/health/refs/health.md +72 -0
- package/skills/platform/health/refs/sources.md +302 -0
- package/skills/platform/incident/refs/incident.md +136 -0
- package/skills/platform/infra/refs/infra.md +104 -0
- package/skills/platform/observability/SKILL.md +128 -0
- package/skills/platform/observability/refs/toolchain-discovery.md +243 -0
- package/skills/platform/prereq-doctor/SKILL.md +57 -0
- package/skills/platform/prereq-doctor/refs/tool-registry.md +58 -0
- package/skills/platform/traces/refs/traces.md +101 -0
- package/skills/product/acceptance-criteria/SKILL.md +193 -0
- package/skills/product/acceptance-criteria/refs/ac-anti-patterns.md +291 -0
- package/skills/product/acceptance-criteria/refs/ac-examples.md +277 -0
- package/skills/product/accessibility/SKILL.md +197 -0
- package/skills/product/accessibility/refs/aria-patterns-basics.md +250 -0
- package/skills/product/accessibility/refs/aria-patterns-dynamic.md +187 -0
- package/skills/product/accessibility/refs/aria-patterns-interactive.md +246 -0
- package/skills/product/accessibility/refs/audit-report-findings.md +248 -0
- package/skills/product/accessibility/refs/audit-report-results.md +276 -0
- package/skills/product/accessibility/refs/common-violations-html-css.md +270 -0
- package/skills/product/accessibility/refs/common-violations-interactive.md +303 -0
- package/skills/product/accessibility/refs/keyboard-testing-basics.md +222 -0
- package/skills/product/accessibility/refs/keyboard-testing-patterns.md +173 -0
- package/skills/product/accessibility/refs/screen-reader-testing-advanced.md +294 -0
- package/skills/product/accessibility/refs/screen-reader-testing-commands.md +255 -0
- package/skills/product/accessibility/refs/wcag-checklist.md +262 -0
- package/skills/product/analyze/SKILL.md +164 -0
- package/skills/product/analyze/refs/algorithms.md +238 -0
- package/skills/product/analyze/refs/sentiment-patterns.md +180 -0
- package/skills/product/imagery/SKILL.md +97 -0
- package/skills/product/imagery/alter/SKILL.md +105 -0
- package/skills/product/imagery/alter/refs/editing-reference.md +146 -0
- package/skills/product/imagery/create/SKILL.md +109 -0
- package/skills/product/imagery/create/refs/prompt-engineering.md +112 -0
- package/skills/product/imagery/create/refs/provider-reference.md +219 -0
- package/skills/product/imagery/review/SKILL.md +101 -0
- package/skills/product/imagery/review/refs/analysis-lenses.md +136 -0
- package/skills/product/imagery/review/refs/quality-gates.md +153 -0
- package/skills/product/imagery/scripts/analyze.sh +23 -0
- package/skills/product/imagery/scripts/edit.sh +14 -0
- package/skills/product/imagery/scripts/generate.sh +13 -0
- package/skills/product/imagery/scripts/provider.py +787 -0
- package/skills/product/listen/SKILL.md +175 -0
- package/skills/product/listen/refs/channels.md +220 -0
- package/skills/product/mockup/SKILL.md +106 -0
- package/skills/product/mockup/refs/templates.md +95 -0
- package/skills/product/refs/a11y.md +109 -0
- package/skills/product/refs/acceptance.md +79 -0
- package/skills/product/refs/align.md +67 -0
- package/skills/product/refs/analyze.md +77 -0
- package/skills/product/refs/elicit.md +67 -0
- package/skills/product/refs/listen.md +58 -0
- package/skills/product/refs/mockup.md +77 -0
- package/skills/product/refs/screenshot.md +59 -0
- package/skills/product/refs/strategy.md +77 -0
- package/skills/product/refs/synthesize.md +54 -0
- package/skills/product/refs/ux-review.md +68 -0
- package/skills/product/refs/ux.md +101 -0
- package/skills/product/requirements-analysis/SKILL.md +136 -0
- package/skills/product/requirements-analysis/refs/requirements-example-auth.md +264 -0
- package/skills/product/requirements-analysis/refs/requirements-example-export-integration.md +88 -0
- package/skills/product/requirements-analysis/refs/requirements-output-format-template.md +247 -0
- package/skills/product/requirements-analysis/refs/user-story-guide.md +296 -0
- package/skills/product/requirements-graph/SKILL.md +155 -0
- package/skills/product/requirements-graph/refs/examples.md +223 -0
- package/skills/product/requirements-graph/refs/schema.md +162 -0
- package/skills/product/requirements-migrate/SKILL.md +90 -0
- package/skills/product/requirements-navigate/SKILL.md +141 -0
- package/skills/product/requirements-navigate/refs/meta-templates.md +141 -0
- package/skills/product/screenshot/SKILL.md +147 -0
- package/skills/product/strategy/SKILL.md +198 -0
- package/skills/product/strategy/refs/frameworks.md +295 -0
- package/skills/product/strategy/refs/templates-competitive-market.md +299 -0
- package/skills/product/strategy/refs/templates-roi-value.md +207 -0
- package/skills/product/synthesize/SKILL.md +116 -0
- package/skills/product/synthesize/refs/journey-mapping.md +212 -0
- package/skills/product/synthesize/refs/prioritization.md +193 -0
- package/skills/product/ux-review/SKILL.md +164 -0
- package/skills/product/visual-direction/SKILL.md +137 -0
- package/skills/product/visual-review/SKILL.md +131 -0
- package/skills/runtime-exec/SKILL.md +138 -0
- package/skills/search/codebase-narrator/SKILL.md +131 -0
- package/skills/search/codebase-narrator/refs/output-template.md +100 -0
- package/skills/smaht/SKILL.md +72 -0
- package/skills/smaht/discovery/SKILL.md +65 -0
- package/skills/smaht/intent/SKILL.md +89 -0
- package/skills/smaht/propose-skills/SKILL.md +142 -0
- package/skills/smaht/refs/state.md +76 -0
- package/skills/swarm/SKILL.md +132 -0
- package/skills/swarm/refs/fan-out.md +104 -0
- package/skills/swarm/refs/independent-verification.md +86 -0
- package/skills/swarm/refs/receipts-and-evidence.md +69 -0
- package/skills/swarm/refs/recon-synthesis.md +87 -0
- package/skills/swarm/refs/ship-discipline.md +90 -0
- package/skills/wickedizer/SKILL.md +147 -0
- package/skills/wickedizer/refs/artifact-types.md +238 -0
- package/skills/wickedizer/refs/complexity.md +278 -0
- package/skills/wickedizer/refs/de-ai-pass.md +140 -0
- package/skills/wickedizer/refs/failure-modes.md +249 -0
- package/skills/wickedizer/refs/patterns.md +282 -0
- package/skills/wickedizer/refs/review.md +221 -0
- package/skills/wickedizer/refs/structure.md +282 -0
- package/skills/wickedizer/refs/voice-profile.md +125 -0
- package/skills/workflow/SKILL.md +132 -0
- package/skills/workflow/refs/archetype-adjustments.md +205 -0
- package/skills/workflow/refs/evidence.md +58 -0
- package/skills/workflow/refs/integration.md +130 -0
- package/skills/workflow/refs/risk-dimension-signals.md +260 -0
- package/skills/workflow/refs/scoring-rubric.md +49 -0
- package/skills/workflow/refs/signal-keywords.md +176 -0
- package/skills/workflow/refs/specialist-routing-rules.md +159 -0
- package/skills/worktrees/SKILL.md +87 -0
- package/skills/worktrees/refs/recipes.md +96 -0
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
qe/coverage_tracker.py — Emit-side coverage delta tracker.
|
|
4
|
+
|
|
5
|
+
Reads standard coverage reports (Cobertura XML, coverage.py JSON),
|
|
6
|
+
persists the last-measured line rate per project in DomainStore, and
|
|
7
|
+
emits `wicked.coverage.changed` on the bus when the delta is non-zero.
|
|
8
|
+
|
|
9
|
+
Design constraints:
|
|
10
|
+
* stdlib-only — no `coverage`, `pytest-cov`, third-party parsers.
|
|
11
|
+
* Fail-open everywhere — a missing report, malformed XML, absent bus,
|
|
12
|
+
and DomainStore write failures are all silent no-ops. Callers never
|
|
13
|
+
observe an exception from `track_and_emit`.
|
|
14
|
+
* Idempotent — measuring the same report twice produces one emit on
|
|
15
|
+
the first run (delta vs. no prior record) and zero on the second
|
|
16
|
+
(delta == 0, so nothing changed).
|
|
17
|
+
* Tier 1 + Tier 2 payload only — aggregate line rate / lines counts,
|
|
18
|
+
never per-file breakdowns.
|
|
19
|
+
* Path-traversal safe — `project_id` is sanitized via the same regex
|
|
20
|
+
shape used by `scripts/qe/_bus_consumers.py` before any storage key
|
|
21
|
+
or filesystem interaction.
|
|
22
|
+
|
|
23
|
+
CLI:
|
|
24
|
+
python3 coverage_tracker.py [--project-id SLUG] [--chain-id ID] [--json]
|
|
25
|
+
|
|
26
|
+
When invoked without `--project-id`, the tracker resolves the active
|
|
27
|
+
project from SessionState (`active_project_id`) and defaults to
|
|
28
|
+
``"default"`` when no session exists.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
import argparse
|
|
34
|
+
import json
|
|
35
|
+
import logging
|
|
36
|
+
import re
|
|
37
|
+
import sys
|
|
38
|
+
import xml.etree.ElementTree as ET
|
|
39
|
+
from datetime import datetime, timezone
|
|
40
|
+
from pathlib import Path
|
|
41
|
+
from typing import Any, Callable, Dict, Optional, Tuple
|
|
42
|
+
|
|
43
|
+
# Ensure scripts/ is on path so _domain_store, _bus, _session are importable
|
|
44
|
+
# when this module is invoked directly (`python3 scripts/qe/coverage_tracker.py`).
|
|
45
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
46
|
+
|
|
47
|
+
logger = logging.getLogger("wicked-qe.coverage-tracker")
|
|
48
|
+
|
|
49
|
+
# Project id sanitation — matches scripts/qe/_bus_consumers.py. Keeps the
|
|
50
|
+
# filesystem slug deterministic and rejects path-traversal attempts.
|
|
51
|
+
_PROJECT_ID_ALLOWED = re.compile(r"[^a-zA-Z0-9_-]")
|
|
52
|
+
_PROJECT_ID_MAX_LEN = 64
|
|
53
|
+
|
|
54
|
+
# Report discovery — ordered by likelihood. First hit wins; each path is
|
|
55
|
+
# resolved relative to the cwd so invocations from a scenario runner or a
|
|
56
|
+
# repo root behave the same way.
|
|
57
|
+
_DEFAULT_SEARCH_PATHS: Tuple[str, ...] = (
|
|
58
|
+
"coverage.xml",
|
|
59
|
+
"coverage.json",
|
|
60
|
+
"htmlcov/coverage.xml",
|
|
61
|
+
"reports/coverage.xml",
|
|
62
|
+
"reports/coverage.json",
|
|
63
|
+
".coverage/coverage.xml",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Storage collection inside DomainStore("wicked-qe"). One record per
|
|
67
|
+
# sanitized project_id.
|
|
68
|
+
_STORE_DOMAIN = "wicked-qe"
|
|
69
|
+
_STORE_SOURCE = "coverage"
|
|
70
|
+
|
|
71
|
+
# Emitted event name — present in BUS_EVENT_MAP under subdomain qe.coverage.
|
|
72
|
+
_EVENT_TYPE = "wicked.coverage.changed"
|
|
73
|
+
|
|
74
|
+
# Delta threshold — anything within this tolerance is treated as zero so
|
|
75
|
+
# rounding noise in XML/JSON parsers does not produce spurious emits.
|
|
76
|
+
_DELTA_EPSILON = 1e-9
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# ---------------------------------------------------------------------------
|
|
80
|
+
# Parsers — Cobertura XML and coverage.py JSON.
|
|
81
|
+
# ---------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
def parse_coverage_xml(path: Path) -> Optional[Dict[str, Any]]:
|
|
84
|
+
"""Parse a Cobertura-format ``coverage.xml`` file.
|
|
85
|
+
|
|
86
|
+
Cobertura (and coverage.py's ``--cov-report=xml`` output) encodes
|
|
87
|
+
totals on the root ``<coverage>`` element as ``line-rate`` /
|
|
88
|
+
``branch-rate`` / ``lines-covered`` / ``lines-valid`` attributes.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Normalized metrics dict, or None on any parse/IO error.
|
|
92
|
+
"""
|
|
93
|
+
try:
|
|
94
|
+
tree = ET.parse(str(path))
|
|
95
|
+
except (ET.ParseError, OSError) as exc:
|
|
96
|
+
logger.debug(f"coverage xml parse failed: {path} ({exc})")
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
root = tree.getroot()
|
|
100
|
+
if root is None:
|
|
101
|
+
return None
|
|
102
|
+
|
|
103
|
+
# Cobertura attributes are strings; coverage.py emits floats formatted
|
|
104
|
+
# with trailing zeros (e.g. "0.8300"). Catch malformed attrs.
|
|
105
|
+
try:
|
|
106
|
+
line_rate = float(root.attrib.get("line-rate", "0") or 0)
|
|
107
|
+
branch_rate = float(root.attrib.get("branch-rate", "0") or 0)
|
|
108
|
+
lines_covered = int(float(root.attrib.get("lines-covered", "0") or 0))
|
|
109
|
+
lines_total = int(float(root.attrib.get("lines-valid", "0") or 0))
|
|
110
|
+
except (TypeError, ValueError) as exc:
|
|
111
|
+
logger.debug(f"coverage xml attr parse failed: {path} ({exc})")
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
"line_rate": _clamp_rate(line_rate),
|
|
116
|
+
"branch_rate": _clamp_rate(branch_rate),
|
|
117
|
+
"lines_covered": max(0, lines_covered),
|
|
118
|
+
"lines_total": max(0, lines_total),
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def parse_coverage_json(path: Path) -> Optional[Dict[str, Any]]:
|
|
123
|
+
"""Parse coverage.py's ``coverage.json`` output.
|
|
124
|
+
|
|
125
|
+
The JSON report emits a ``totals`` block with ``percent_covered``
|
|
126
|
+
(0..100), ``covered_lines``, ``num_statements``, and
|
|
127
|
+
``percent_covered_display``. Branch coverage, when present, is under
|
|
128
|
+
``totals.percent_covered_branches`` or derivable from ``num_branches``.
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
Normalized metrics dict, or None on any parse/IO error.
|
|
132
|
+
"""
|
|
133
|
+
try:
|
|
134
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
135
|
+
except (json.JSONDecodeError, OSError) as exc:
|
|
136
|
+
logger.debug(f"coverage json parse failed: {path} ({exc})")
|
|
137
|
+
return None
|
|
138
|
+
|
|
139
|
+
if not isinstance(data, dict):
|
|
140
|
+
return None
|
|
141
|
+
|
|
142
|
+
totals = data.get("totals")
|
|
143
|
+
if not isinstance(totals, dict):
|
|
144
|
+
return None
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
# percent_covered is 0..100; normalize to 0..1 to match XML shape.
|
|
148
|
+
percent_covered = float(totals.get("percent_covered", 0) or 0)
|
|
149
|
+
line_rate = percent_covered / 100.0
|
|
150
|
+
lines_covered = int(totals.get("covered_lines", 0) or 0)
|
|
151
|
+
lines_total = int(totals.get("num_statements", 0) or 0)
|
|
152
|
+
|
|
153
|
+
# Branch rate — coverage.py names this inconsistently across
|
|
154
|
+
# versions; check both common keys before falling back to 0.
|
|
155
|
+
branch_pct_raw = (
|
|
156
|
+
totals.get("percent_covered_branches")
|
|
157
|
+
if "percent_covered_branches" in totals
|
|
158
|
+
else totals.get("branch_coverage", 0)
|
|
159
|
+
)
|
|
160
|
+
branch_rate = float(branch_pct_raw or 0) / 100.0
|
|
161
|
+
except (TypeError, ValueError) as exc:
|
|
162
|
+
logger.debug(f"coverage json totals parse failed: {path} ({exc})")
|
|
163
|
+
return None
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
"line_rate": _clamp_rate(line_rate),
|
|
167
|
+
"branch_rate": _clamp_rate(branch_rate),
|
|
168
|
+
"lines_covered": max(0, lines_covered),
|
|
169
|
+
"lines_total": max(0, lines_total),
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _clamp_rate(value: float) -> float:
|
|
174
|
+
"""Clamp a rate into 0..1 — coverage tools very occasionally emit
|
|
175
|
+
slightly-out-of-range values when a file has zero statements."""
|
|
176
|
+
if value < 0:
|
|
177
|
+
return 0.0
|
|
178
|
+
if value > 1:
|
|
179
|
+
return 1.0
|
|
180
|
+
return value
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
# ---------------------------------------------------------------------------
|
|
184
|
+
# Report discovery
|
|
185
|
+
# ---------------------------------------------------------------------------
|
|
186
|
+
|
|
187
|
+
def find_coverage_report(
|
|
188
|
+
search_paths: Optional[Tuple[str, ...]] = None,
|
|
189
|
+
cwd: Optional[Path] = None,
|
|
190
|
+
) -> Tuple[Optional[Path], Optional[Callable[[Path], Optional[Dict[str, Any]]]]]:
|
|
191
|
+
"""Scan for a coverage report in the cwd tree.
|
|
192
|
+
|
|
193
|
+
Returns the first matching (path, parser) tuple, or ``(None, None)``
|
|
194
|
+
when no standard report is present.
|
|
195
|
+
"""
|
|
196
|
+
base = (cwd or Path.cwd()).resolve()
|
|
197
|
+
candidates = search_paths or _DEFAULT_SEARCH_PATHS
|
|
198
|
+
|
|
199
|
+
for rel in candidates:
|
|
200
|
+
candidate = (base / rel).resolve()
|
|
201
|
+
# Guard against symlink escapes — the candidate must stay under
|
|
202
|
+
# the working directory we were asked to scan.
|
|
203
|
+
try:
|
|
204
|
+
candidate.relative_to(base)
|
|
205
|
+
except ValueError:
|
|
206
|
+
continue
|
|
207
|
+
if not candidate.is_file():
|
|
208
|
+
continue
|
|
209
|
+
parser = _parser_for(candidate)
|
|
210
|
+
if parser is None:
|
|
211
|
+
continue
|
|
212
|
+
return candidate, parser
|
|
213
|
+
|
|
214
|
+
return None, None
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _parser_for(path: Path) -> Optional[Callable[[Path], Optional[Dict[str, Any]]]]:
|
|
218
|
+
suffix = path.suffix.lower()
|
|
219
|
+
if suffix == ".xml":
|
|
220
|
+
return parse_coverage_xml
|
|
221
|
+
if suffix == ".json":
|
|
222
|
+
return parse_coverage_json
|
|
223
|
+
return None
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
# ---------------------------------------------------------------------------
|
|
227
|
+
# Persistence — DomainStore("wicked-qe") collection "coverage"
|
|
228
|
+
# ---------------------------------------------------------------------------
|
|
229
|
+
|
|
230
|
+
def load_previous(project_id: str) -> Optional[Dict[str, Any]]:
|
|
231
|
+
"""Load the last-recorded coverage entry for ``project_id``.
|
|
232
|
+
|
|
233
|
+
Returns ``{"line_rate": float}`` or None when no record exists or the
|
|
234
|
+
DomainStore is unavailable. Follows the crew phase_manager pattern of
|
|
235
|
+
failing-open on any DomainStore error.
|
|
236
|
+
"""
|
|
237
|
+
slug = _sanitize_project_id(project_id)
|
|
238
|
+
if slug is None:
|
|
239
|
+
return None
|
|
240
|
+
|
|
241
|
+
try:
|
|
242
|
+
from _domain_store import DomainStore
|
|
243
|
+
|
|
244
|
+
store = DomainStore(_STORE_DOMAIN)
|
|
245
|
+
record = store.get(_STORE_SOURCE, slug)
|
|
246
|
+
except Exception as exc:
|
|
247
|
+
logger.debug(f"load_previous: DomainStore error (non-fatal): {exc}")
|
|
248
|
+
return None
|
|
249
|
+
|
|
250
|
+
if not record:
|
|
251
|
+
return None
|
|
252
|
+
|
|
253
|
+
# Only surface the field we need — older records may carry extra
|
|
254
|
+
# keys from previous schema iterations.
|
|
255
|
+
line_rate = record.get("line_rate")
|
|
256
|
+
if not isinstance(line_rate, (int, float)):
|
|
257
|
+
return None
|
|
258
|
+
return {"line_rate": float(line_rate)}
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def store_current(project_id: str, line_rate: float, chain_id: str = "") -> bool:
|
|
262
|
+
"""Upsert the coverage record for ``project_id``.
|
|
263
|
+
|
|
264
|
+
Returns True on successful write, False on any DomainStore error.
|
|
265
|
+
Fail-open: errors are logged at debug and swallowed.
|
|
266
|
+
"""
|
|
267
|
+
slug = _sanitize_project_id(project_id)
|
|
268
|
+
if slug is None:
|
|
269
|
+
return False
|
|
270
|
+
|
|
271
|
+
record = {
|
|
272
|
+
"id": slug,
|
|
273
|
+
"line_rate": round(float(line_rate), 6),
|
|
274
|
+
"measured_at": _utcnow_iso(),
|
|
275
|
+
"chain_id": chain_id or "",
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
try:
|
|
279
|
+
from _domain_store import DomainStore
|
|
280
|
+
|
|
281
|
+
store = DomainStore(_STORE_DOMAIN)
|
|
282
|
+
existing = store.get(_STORE_SOURCE, slug)
|
|
283
|
+
if existing is None:
|
|
284
|
+
store.create(_STORE_SOURCE, record)
|
|
285
|
+
else:
|
|
286
|
+
# Drop the id from the diff — create() assigned it; update()
|
|
287
|
+
# does not need a new id.
|
|
288
|
+
diff = {k: v for k, v in record.items() if k != "id"}
|
|
289
|
+
store.update(_STORE_SOURCE, slug, diff)
|
|
290
|
+
return True
|
|
291
|
+
except Exception as exc:
|
|
292
|
+
logger.debug(f"store_current: DomainStore error (non-fatal): {exc}")
|
|
293
|
+
return False
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
# ---------------------------------------------------------------------------
|
|
297
|
+
# Main entry point
|
|
298
|
+
# ---------------------------------------------------------------------------
|
|
299
|
+
|
|
300
|
+
def track_and_emit(
|
|
301
|
+
project_id: Optional[str] = None,
|
|
302
|
+
chain_id: Optional[str] = None,
|
|
303
|
+
search_paths: Optional[Tuple[str, ...]] = None,
|
|
304
|
+
cwd: Optional[Path] = None,
|
|
305
|
+
) -> Dict[str, Any]:
|
|
306
|
+
"""Find → parse → diff → emit → store.
|
|
307
|
+
|
|
308
|
+
Returns a summary dict. On any failure path a negative result shape
|
|
309
|
+
``{"ok": False, "reason": "..."}`` is returned — the caller never
|
|
310
|
+
sees an exception.
|
|
311
|
+
"""
|
|
312
|
+
# Resolve project_id from session state when not supplied. The session
|
|
313
|
+
# record uses ``active_project_id`` as the local project slug.
|
|
314
|
+
project_slug = _resolve_project_id(project_id)
|
|
315
|
+
if project_slug is None:
|
|
316
|
+
return {"ok": False, "reason": "invalid-project-id"}
|
|
317
|
+
|
|
318
|
+
resolved_chain = _resolve_chain_id(chain_id)
|
|
319
|
+
|
|
320
|
+
path, parser = find_coverage_report(search_paths=search_paths, cwd=cwd)
|
|
321
|
+
if path is None or parser is None:
|
|
322
|
+
return {"ok": False, "reason": "no-coverage-report"}
|
|
323
|
+
|
|
324
|
+
metrics = None
|
|
325
|
+
try:
|
|
326
|
+
metrics = parser(path)
|
|
327
|
+
except Exception as exc:
|
|
328
|
+
# Defensive — parsers already catch known errors, but any future
|
|
329
|
+
# addition should still fail-open.
|
|
330
|
+
logger.debug(f"coverage parser raised: {exc}")
|
|
331
|
+
return {"ok": False, "reason": "parse-error", "path": str(path)}
|
|
332
|
+
|
|
333
|
+
if not metrics:
|
|
334
|
+
return {"ok": False, "reason": "parse-error", "path": str(path)}
|
|
335
|
+
|
|
336
|
+
after = float(metrics["line_rate"])
|
|
337
|
+
previous = load_previous(project_slug)
|
|
338
|
+
before = float(previous["line_rate"]) if previous else 0.0
|
|
339
|
+
delta = after - before
|
|
340
|
+
|
|
341
|
+
summary: Dict[str, Any] = {
|
|
342
|
+
"ok": True,
|
|
343
|
+
"project_id": project_slug,
|
|
344
|
+
"path": str(path),
|
|
345
|
+
"before": before,
|
|
346
|
+
"after": after,
|
|
347
|
+
"delta": delta,
|
|
348
|
+
"emitted": False,
|
|
349
|
+
"first_measurement": previous is None,
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if abs(delta) <= _DELTA_EPSILON:
|
|
353
|
+
# No change — still refresh the stored record so ``measured_at``
|
|
354
|
+
# stays current and chain_id mirrors the latest run. This is
|
|
355
|
+
# cheap and keeps the audit trail useful.
|
|
356
|
+
store_current(project_slug, after, chain_id=resolved_chain)
|
|
357
|
+
return summary
|
|
358
|
+
|
|
359
|
+
# Delta is non-zero — emit then store. Order matters: if we store
|
|
360
|
+
# first and then fail to emit, the next run sees delta == 0 and will
|
|
361
|
+
# swallow the event. Emitting first means a transient bus failure
|
|
362
|
+
# still preserves the old baseline, so the delta re-fires next time.
|
|
363
|
+
_emit_coverage_changed(before, after, delta, resolved_chain)
|
|
364
|
+
store_current(project_slug, after, chain_id=resolved_chain)
|
|
365
|
+
summary["emitted"] = True
|
|
366
|
+
return summary
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def _emit_coverage_changed(
|
|
370
|
+
before: float,
|
|
371
|
+
after: float,
|
|
372
|
+
delta: float,
|
|
373
|
+
chain_id: str,
|
|
374
|
+
) -> None:
|
|
375
|
+
"""Fire ``wicked.coverage.changed`` — fail-open."""
|
|
376
|
+
try:
|
|
377
|
+
from _bus import emit_event
|
|
378
|
+
|
|
379
|
+
emit_event(
|
|
380
|
+
_EVENT_TYPE,
|
|
381
|
+
{
|
|
382
|
+
"before_pct": round(before * 100, 2),
|
|
383
|
+
"after_pct": round(after * 100, 2),
|
|
384
|
+
"delta": round(delta * 100, 2),
|
|
385
|
+
"chain_id": chain_id or "",
|
|
386
|
+
},
|
|
387
|
+
chain_id=chain_id or None,
|
|
388
|
+
)
|
|
389
|
+
except Exception as exc:
|
|
390
|
+
logger.debug(f"emit coverage changed failed (non-fatal): {exc}")
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
# ---------------------------------------------------------------------------
|
|
394
|
+
# Helpers
|
|
395
|
+
# ---------------------------------------------------------------------------
|
|
396
|
+
|
|
397
|
+
def _sanitize_project_id(project_id: Optional[str]) -> Optional[str]:
|
|
398
|
+
"""Reduce ``project_id`` to a filesystem-safe slug or None.
|
|
399
|
+
|
|
400
|
+
Anything outside ``[a-zA-Z0-9_-]`` becomes ``-``. Empty strings,
|
|
401
|
+
non-strings, and oversized inputs are rejected outright.
|
|
402
|
+
"""
|
|
403
|
+
if not isinstance(project_id, str):
|
|
404
|
+
return None
|
|
405
|
+
cleaned = _PROJECT_ID_ALLOWED.sub("-", project_id).strip("-")
|
|
406
|
+
if not cleaned:
|
|
407
|
+
return None
|
|
408
|
+
return cleaned[:_PROJECT_ID_MAX_LEN]
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def _resolve_project_id(project_id: Optional[str]) -> Optional[str]:
|
|
412
|
+
"""Sanitize the supplied project_id or fall back to session state."""
|
|
413
|
+
if project_id:
|
|
414
|
+
return _sanitize_project_id(project_id)
|
|
415
|
+
|
|
416
|
+
# Session fallback — mirrors the pattern used by registry_store.py.
|
|
417
|
+
try:
|
|
418
|
+
from _session import SessionState # type: ignore
|
|
419
|
+
|
|
420
|
+
state = SessionState.load()
|
|
421
|
+
fallback = getattr(state, "active_project_id", None)
|
|
422
|
+
if fallback:
|
|
423
|
+
return _sanitize_project_id(fallback)
|
|
424
|
+
except Exception:
|
|
425
|
+
pass # fail open: SessionState unavailable — fall through to default slug
|
|
426
|
+
|
|
427
|
+
# Last resort — use a stable default slug so we still record
|
|
428
|
+
# something. Better to have one "default" record than lose the signal.
|
|
429
|
+
return "default"
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def _resolve_chain_id(chain_id: Optional[str]) -> str:
|
|
433
|
+
"""Return an explicit chain_id, else read from SessionState, else ''."""
|
|
434
|
+
if chain_id:
|
|
435
|
+
return chain_id
|
|
436
|
+
|
|
437
|
+
try:
|
|
438
|
+
from _session import SessionState # type: ignore
|
|
439
|
+
|
|
440
|
+
state = SessionState.load()
|
|
441
|
+
fallback = getattr(state, "active_chain_id", None) or ""
|
|
442
|
+
return fallback or ""
|
|
443
|
+
except Exception:
|
|
444
|
+
return ""
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
def _utcnow_iso() -> str:
|
|
448
|
+
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
# ---------------------------------------------------------------------------
|
|
452
|
+
# CLI
|
|
453
|
+
# ---------------------------------------------------------------------------
|
|
454
|
+
|
|
455
|
+
def _build_parser() -> argparse.ArgumentParser:
|
|
456
|
+
parser = argparse.ArgumentParser(
|
|
457
|
+
description="Track test-coverage delta and emit wicked.coverage.changed.",
|
|
458
|
+
)
|
|
459
|
+
parser.add_argument(
|
|
460
|
+
"--project-id",
|
|
461
|
+
default=None,
|
|
462
|
+
help="Project slug for the coverage record (defaults to session).",
|
|
463
|
+
)
|
|
464
|
+
parser.add_argument(
|
|
465
|
+
"--chain-id",
|
|
466
|
+
default=None,
|
|
467
|
+
help="Crew causality chain id to attach to the emit (optional).",
|
|
468
|
+
)
|
|
469
|
+
parser.add_argument(
|
|
470
|
+
"--json",
|
|
471
|
+
action="store_true",
|
|
472
|
+
help="Print the summary dict as JSON on stdout.",
|
|
473
|
+
)
|
|
474
|
+
return parser
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def main(argv: Optional[list[str]] = None) -> int:
|
|
478
|
+
args = _build_parser().parse_args(argv)
|
|
479
|
+
summary = track_and_emit(project_id=args.project_id, chain_id=args.chain_id)
|
|
480
|
+
if args.json:
|
|
481
|
+
print(json.dumps(summary, default=str))
|
|
482
|
+
# Exit 0 regardless — fail-open by contract.
|
|
483
|
+
return 0
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
# ---------------------------------------------------------------------------
|
|
487
|
+
# Inline smoke test — ``python3 coverage_tracker.py --selftest`` from the
|
|
488
|
+
# main block below. Uses a small literal Cobertura document so the test
|
|
489
|
+
# is hermetic and doesn't touch DomainStore or the bus.
|
|
490
|
+
# ---------------------------------------------------------------------------
|
|
491
|
+
|
|
492
|
+
_SELFTEST_XML = """<?xml version="1.0" ?>
|
|
493
|
+
<coverage line-rate="0.8300" branch-rate="0.75" lines-covered="83" lines-valid="100" version="7.4.0" timestamp="0">
|
|
494
|
+
<packages>
|
|
495
|
+
<package name="pkg" line-rate="0.83" branch-rate="0.75">
|
|
496
|
+
<classes/>
|
|
497
|
+
</package>
|
|
498
|
+
</packages>
|
|
499
|
+
</coverage>
|
|
500
|
+
"""
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
def _selftest() -> int:
|
|
504
|
+
"""Exercise the XML parser against a literal fixture."""
|
|
505
|
+
import tempfile
|
|
506
|
+
|
|
507
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
508
|
+
tmp_path = Path(tmpdir) / "coverage.xml"
|
|
509
|
+
tmp_path.write_text(_SELFTEST_XML, encoding="utf-8")
|
|
510
|
+
metrics = parse_coverage_xml(tmp_path)
|
|
511
|
+
|
|
512
|
+
expected = {
|
|
513
|
+
"line_rate": 0.83,
|
|
514
|
+
"branch_rate": 0.75,
|
|
515
|
+
"lines_covered": 83,
|
|
516
|
+
"lines_total": 100,
|
|
517
|
+
}
|
|
518
|
+
if metrics != expected:
|
|
519
|
+
print(f"selftest FAIL: got {metrics!r}, expected {expected!r}", file=sys.stderr)
|
|
520
|
+
return 1
|
|
521
|
+
|
|
522
|
+
# Delta epsilon check — re-measuring the same number should be a no-op.
|
|
523
|
+
delta = expected["line_rate"] - 0.83
|
|
524
|
+
if abs(delta) > _DELTA_EPSILON:
|
|
525
|
+
print("selftest FAIL: epsilon check did not treat 0.83-0.83 as zero", file=sys.stderr)
|
|
526
|
+
return 1
|
|
527
|
+
|
|
528
|
+
print("selftest OK")
|
|
529
|
+
return 0
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
if __name__ == "__main__":
|
|
533
|
+
if "--selftest" in sys.argv:
|
|
534
|
+
sys.exit(_selftest())
|
|
535
|
+
sys.exit(main())
|