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,216 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
guard_profiles.py — Profile definitions + auto-select logic for the
|
|
4
|
+
autonomous session-close guard pipeline (Issue #448).
|
|
5
|
+
|
|
6
|
+
Three tiered profiles govern how much work the guard pipeline does at
|
|
7
|
+
session close or phase checkpoints:
|
|
8
|
+
|
|
9
|
+
scalpel (~1s) — always runs. Regex/line-pattern scans only.
|
|
10
|
+
Debug artifacts + R1-R6 surface-level bulletproof
|
|
11
|
+
heuristics. Cheap enough to run on every Stop hook.
|
|
12
|
+
|
|
13
|
+
standard (~5s) — runs at build-phase close or session close after
|
|
14
|
+
substantial changes. Adds ADR constraint scan,
|
|
15
|
+
spec-drift semantic review (optional), and unresolved
|
|
16
|
+
skip-reeval entries.
|
|
17
|
+
|
|
18
|
+
deep (~30s) — runs on explicit invocation or release-branch sessions.
|
|
19
|
+
Full checks + deeper semantic review pass.
|
|
20
|
+
|
|
21
|
+
Auto-select rules (one-liner per profile):
|
|
22
|
+
|
|
23
|
+
deep — on release/main branch sessions OR explicit WG_GUARD_PROFILE=deep
|
|
24
|
+
standard — build-phase just closed, or >= 10 changed files in diff
|
|
25
|
+
scalpel — everything else (default, fail-open minimum)
|
|
26
|
+
|
|
27
|
+
The selector never raises; every branch falls back to ``scalpel`` on error.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
import os
|
|
33
|
+
import subprocess
|
|
34
|
+
from dataclasses import dataclass, field
|
|
35
|
+
from pathlib import Path
|
|
36
|
+
from typing import Iterable, List, Optional
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# ---------------------------------------------------------------------------
|
|
40
|
+
# Budgets — seconds
|
|
41
|
+
# ---------------------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
SCALPEL_BUDGET_SECONDS = 1.0
|
|
44
|
+
STANDARD_BUDGET_SECONDS = 5.0
|
|
45
|
+
DEEP_BUDGET_SECONDS = 30.0
|
|
46
|
+
|
|
47
|
+
# Threshold of changed files that promotes scalpel → standard.
|
|
48
|
+
_SUBSTANTIAL_CHANGE_THRESHOLD = 10
|
|
49
|
+
|
|
50
|
+
# Branch names that auto-promote to deep.
|
|
51
|
+
_RELEASE_BRANCH_NAMES = frozenset({"main", "master", "release", "production"})
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# ---------------------------------------------------------------------------
|
|
55
|
+
# Profile definitions
|
|
56
|
+
# ---------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
@dataclass(frozen=True)
|
|
59
|
+
class GuardProfile:
|
|
60
|
+
"""Runtime profile for the guard pipeline."""
|
|
61
|
+
|
|
62
|
+
name: str
|
|
63
|
+
budget_seconds: float
|
|
64
|
+
checks: tuple # tuple of check-name strings
|
|
65
|
+
include_semantic_review: bool
|
|
66
|
+
description: str
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# Check names — must match keys in guard_pipeline.CHECK_REGISTRY
|
|
70
|
+
_SCALPEL_CHECKS = ("bulletproof_scan", "debug_artifacts")
|
|
71
|
+
_STANDARD_CHECKS = (
|
|
72
|
+
"bulletproof_scan",
|
|
73
|
+
"debug_artifacts",
|
|
74
|
+
"adr_constraints",
|
|
75
|
+
"semantic_review",
|
|
76
|
+
"skip_log",
|
|
77
|
+
)
|
|
78
|
+
_DEEP_CHECKS = _STANDARD_CHECKS # same check set, deeper semantic review
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
SCALPEL = GuardProfile(
|
|
82
|
+
name="scalpel",
|
|
83
|
+
budget_seconds=SCALPEL_BUDGET_SECONDS,
|
|
84
|
+
checks=_SCALPEL_CHECKS,
|
|
85
|
+
include_semantic_review=False,
|
|
86
|
+
description="Fast regex-only scan (<1s). Always runs.",
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
STANDARD = GuardProfile(
|
|
90
|
+
name="standard",
|
|
91
|
+
budget_seconds=STANDARD_BUDGET_SECONDS,
|
|
92
|
+
checks=_STANDARD_CHECKS,
|
|
93
|
+
include_semantic_review=True,
|
|
94
|
+
description="Build-phase close or substantial changes (~5s).",
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
DEEP = GuardProfile(
|
|
98
|
+
name="deep",
|
|
99
|
+
budget_seconds=DEEP_BUDGET_SECONDS,
|
|
100
|
+
checks=_DEEP_CHECKS,
|
|
101
|
+
include_semantic_review=True,
|
|
102
|
+
description="Release branch or explicit invocation (~30s).",
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
_PROFILES_BY_NAME = {p.name: p for p in (SCALPEL, STANDARD, DEEP)}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def get_profile(name: str) -> GuardProfile:
|
|
109
|
+
"""Return the named profile, falling back to scalpel on miss."""
|
|
110
|
+
return _PROFILES_BY_NAME.get((name or "").strip().lower(), SCALPEL)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
# ---------------------------------------------------------------------------
|
|
114
|
+
# Auto-select logic
|
|
115
|
+
# ---------------------------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
def _current_branch(cwd: Optional[Path] = None) -> Optional[str]:
|
|
118
|
+
"""Return the current git branch, or None on error."""
|
|
119
|
+
try:
|
|
120
|
+
result = subprocess.run(
|
|
121
|
+
["git", "rev-parse", "--abbrev-ref", "HEAD"],
|
|
122
|
+
cwd=str(cwd) if cwd else None,
|
|
123
|
+
capture_output=True,
|
|
124
|
+
text=True,
|
|
125
|
+
timeout=2,
|
|
126
|
+
)
|
|
127
|
+
if result.returncode == 0:
|
|
128
|
+
branch = result.stdout.strip()
|
|
129
|
+
return branch or None
|
|
130
|
+
except Exception:
|
|
131
|
+
pass # fail open — callers treat None as "unknown branch"
|
|
132
|
+
return None
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _count_changed_files(cwd: Optional[Path] = None) -> int:
|
|
136
|
+
"""Count changed files vs HEAD (staged + unstaged + untracked tracked)."""
|
|
137
|
+
try:
|
|
138
|
+
result = subprocess.run(
|
|
139
|
+
["git", "status", "--porcelain"],
|
|
140
|
+
cwd=str(cwd) if cwd else None,
|
|
141
|
+
capture_output=True,
|
|
142
|
+
text=True,
|
|
143
|
+
timeout=2,
|
|
144
|
+
)
|
|
145
|
+
if result.returncode == 0:
|
|
146
|
+
lines = [l for l in result.stdout.splitlines() if l.strip()]
|
|
147
|
+
return len(lines)
|
|
148
|
+
except Exception:
|
|
149
|
+
pass # fail open — treat unknowable diffs as "zero" (safest profile)
|
|
150
|
+
return 0
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def auto_select(
|
|
154
|
+
*,
|
|
155
|
+
build_phase_just_closed: bool = False,
|
|
156
|
+
explicit_profile: Optional[str] = None,
|
|
157
|
+
cwd: Optional[Path] = None,
|
|
158
|
+
) -> GuardProfile:
|
|
159
|
+
"""Auto-select the guard profile based on session context.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
build_phase_just_closed: True if the crew build phase was just approved
|
|
163
|
+
(promotes scalpel → standard).
|
|
164
|
+
explicit_profile: Overrides auto-selection. Takes env var
|
|
165
|
+
``WG_GUARD_PROFILE`` into account when not supplied.
|
|
166
|
+
cwd: Working directory for git probes. Defaults to process cwd.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
The selected GuardProfile. Never raises — falls back to SCALPEL on error.
|
|
170
|
+
"""
|
|
171
|
+
# Explicit arg wins
|
|
172
|
+
chosen = (explicit_profile or os.environ.get("WG_GUARD_PROFILE") or "").strip().lower()
|
|
173
|
+
if chosen in _PROFILES_BY_NAME:
|
|
174
|
+
return _PROFILES_BY_NAME[chosen]
|
|
175
|
+
|
|
176
|
+
try:
|
|
177
|
+
branch = _current_branch(cwd)
|
|
178
|
+
if branch and branch in _RELEASE_BRANCH_NAMES:
|
|
179
|
+
return DEEP
|
|
180
|
+
|
|
181
|
+
if build_phase_just_closed:
|
|
182
|
+
return STANDARD
|
|
183
|
+
|
|
184
|
+
changed = _count_changed_files(cwd)
|
|
185
|
+
if changed >= _SUBSTANTIAL_CHANGE_THRESHOLD:
|
|
186
|
+
return STANDARD
|
|
187
|
+
except Exception:
|
|
188
|
+
# Any unexpected error → safest (cheapest) profile
|
|
189
|
+
return SCALPEL
|
|
190
|
+
|
|
191
|
+
return SCALPEL
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
# ---------------------------------------------------------------------------
|
|
195
|
+
# CLI for debugging: `python3 guard_profiles.py --explain`
|
|
196
|
+
# ---------------------------------------------------------------------------
|
|
197
|
+
|
|
198
|
+
if __name__ == "__main__":
|
|
199
|
+
import json
|
|
200
|
+
import sys
|
|
201
|
+
|
|
202
|
+
args = sys.argv[1:]
|
|
203
|
+
if "--explain" in args:
|
|
204
|
+
profile = auto_select(cwd=Path.cwd())
|
|
205
|
+
out = {
|
|
206
|
+
"profile": profile.name,
|
|
207
|
+
"budget_seconds": profile.budget_seconds,
|
|
208
|
+
"checks": list(profile.checks),
|
|
209
|
+
"description": profile.description,
|
|
210
|
+
}
|
|
211
|
+
sys.stdout.write(json.dumps(out, indent=2))
|
|
212
|
+
sys.stdout.write("\n")
|
|
213
|
+
sys.exit(0)
|
|
214
|
+
|
|
215
|
+
sys.stdout.write("Usage: guard_profiles.py --explain\n")
|
|
216
|
+
sys.exit(1)
|
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Contract assertion runner — validates subprocess outputs against JSON schemas.
|
|
3
|
+
|
|
4
|
+
Discovers schemas in schemas/{plugin}/{script-name}.json relative to the plugin
|
|
5
|
+
root, runs the corresponding script with --health-check (if supported) or a
|
|
6
|
+
minimal invocation, and validates the output against the schema.
|
|
7
|
+
|
|
8
|
+
Failure classes:
|
|
9
|
+
timeout — script did not complete within 10s
|
|
10
|
+
empty — script produced 0 bytes or empty JSON
|
|
11
|
+
malformed — output failed schema validation
|
|
12
|
+
pass — output validated successfully
|
|
13
|
+
|
|
14
|
+
Results are persisted via DomainStore("wicked-observability").
|
|
15
|
+
|
|
16
|
+
Usage:
|
|
17
|
+
python3 assert_contracts.py
|
|
18
|
+
python3 assert_contracts.py --plugin wicked-smaht
|
|
19
|
+
python3 assert_contracts.py --json
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import argparse
|
|
23
|
+
import json
|
|
24
|
+
import os
|
|
25
|
+
import subprocess
|
|
26
|
+
import sys
|
|
27
|
+
from datetime import datetime, timezone
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
|
|
30
|
+
# Resolve _domain_store from the scripts/ directory.
|
|
31
|
+
# This file lives at scripts/platform/observability/, so scripts/ is parents[2].
|
|
32
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
|
|
33
|
+
from _domain_store import DomainStore
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# ── Constants ────────────────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
PLUGIN_ROOT = Path(__file__).resolve().parent.parent.parent.parent # scripts/platform/observability/ → scripts/ → repo root
|
|
39
|
+
SCHEMAS_DIR = PLUGIN_ROOT / "schemas"
|
|
40
|
+
PLUGINS_ROOT = PLUGIN_ROOT.parent # …/plugins/
|
|
41
|
+
TIMEOUT_SECONDS = 10
|
|
42
|
+
|
|
43
|
+
_sm = DomainStore("wicked-observability")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# ── Minimal JSON Schema validator ────────────────────────────────────────────
|
|
47
|
+
# Implements: required, type, enum, properties (recursive), items (array)
|
|
48
|
+
# Does NOT require the jsonschema package.
|
|
49
|
+
|
|
50
|
+
_TYPE_MAP = {
|
|
51
|
+
"string": str,
|
|
52
|
+
"integer": int,
|
|
53
|
+
"number": (int, float),
|
|
54
|
+
"boolean": bool,
|
|
55
|
+
"null": type(None),
|
|
56
|
+
"array": list,
|
|
57
|
+
"object": dict,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _validate(value, schema: dict, path: str = "$") -> list[dict]:
|
|
62
|
+
"""Return a list of violation dicts; empty means valid."""
|
|
63
|
+
violations = []
|
|
64
|
+
|
|
65
|
+
# type check
|
|
66
|
+
expected_type = schema.get("type")
|
|
67
|
+
if expected_type:
|
|
68
|
+
# Python's bool is a subclass of int — reject bools for integer/number
|
|
69
|
+
if expected_type in ("integer", "number") and isinstance(value, bool):
|
|
70
|
+
violations.append({
|
|
71
|
+
"field": path,
|
|
72
|
+
"message": "type mismatch",
|
|
73
|
+
"expected": expected_type,
|
|
74
|
+
"actual": "boolean",
|
|
75
|
+
})
|
|
76
|
+
return violations
|
|
77
|
+
py_type = _TYPE_MAP.get(expected_type)
|
|
78
|
+
if py_type and not isinstance(value, py_type):
|
|
79
|
+
# JSON integers satisfy "number"
|
|
80
|
+
if not (expected_type == "number" and isinstance(value, (int, float))):
|
|
81
|
+
actual = type(value).__name__
|
|
82
|
+
violations.append({
|
|
83
|
+
"field": path,
|
|
84
|
+
"message": "type mismatch",
|
|
85
|
+
"expected": expected_type,
|
|
86
|
+
"actual": actual,
|
|
87
|
+
})
|
|
88
|
+
return violations # no point checking sub-constraints on wrong type
|
|
89
|
+
|
|
90
|
+
# enum check
|
|
91
|
+
enum_values = schema.get("enum")
|
|
92
|
+
if enum_values is not None and value not in enum_values:
|
|
93
|
+
violations.append({
|
|
94
|
+
"field": path,
|
|
95
|
+
"message": "value not in enum",
|
|
96
|
+
"expected": enum_values,
|
|
97
|
+
"actual": value,
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
# object properties + required
|
|
101
|
+
if isinstance(value, dict):
|
|
102
|
+
required = schema.get("required", [])
|
|
103
|
+
for field in required:
|
|
104
|
+
if field not in value:
|
|
105
|
+
violations.append({
|
|
106
|
+
"field": f"{path}.{field}",
|
|
107
|
+
"message": "required field missing",
|
|
108
|
+
"expected": "present",
|
|
109
|
+
"actual": "null",
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
properties = schema.get("properties", {})
|
|
113
|
+
for prop_name, prop_schema in properties.items():
|
|
114
|
+
if prop_name in value:
|
|
115
|
+
violations.extend(
|
|
116
|
+
_validate(value[prop_name], prop_schema, f"{path}.{prop_name}")
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# array items
|
|
120
|
+
if isinstance(value, list):
|
|
121
|
+
items_schema = schema.get("items")
|
|
122
|
+
if items_schema:
|
|
123
|
+
for i, item in enumerate(value):
|
|
124
|
+
violations.extend(_validate(item, items_schema, f"{path}[{i}]"))
|
|
125
|
+
|
|
126
|
+
return violations
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def validate_against_schema(data, schema: dict) -> list[dict]:
|
|
130
|
+
"""Validate data against schema; return list of violations."""
|
|
131
|
+
return _validate(data, schema)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
# ── Schema discovery ─────────────────────────────────────────────────────────
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def discover_schemas(plugin_filter: str | None = None) -> list[dict]:
|
|
138
|
+
"""
|
|
139
|
+
Walk SCHEMAS_DIR and yield schema descriptor dicts:
|
|
140
|
+
{plugin, script_name, schema_path, script_path | None}
|
|
141
|
+
"""
|
|
142
|
+
if not SCHEMAS_DIR.exists():
|
|
143
|
+
return []
|
|
144
|
+
|
|
145
|
+
descriptors = []
|
|
146
|
+
# Read plugin name from plugin.json (single-plugin repo)
|
|
147
|
+
_plugin_json = PLUGIN_ROOT / ".claude-plugin" / "plugin.json"
|
|
148
|
+
_default_plugin = "wicked-garden"
|
|
149
|
+
if _plugin_json.exists():
|
|
150
|
+
try:
|
|
151
|
+
_default_plugin = json.loads(_plugin_json.read_text()).get("name", _default_plugin)
|
|
152
|
+
except Exception:
|
|
153
|
+
pass # fail open
|
|
154
|
+
|
|
155
|
+
for schema_path in sorted(SCHEMAS_DIR.rglob("*.json")):
|
|
156
|
+
parts = schema_path.relative_to(SCHEMAS_DIR).parts
|
|
157
|
+
if len(parts) == 2:
|
|
158
|
+
# Legacy layout: schemas/{plugin}/{script-name}.json
|
|
159
|
+
plugin_name, schema_file = parts
|
|
160
|
+
elif len(parts) == 1:
|
|
161
|
+
# Flat layout: schemas/{script-name}.json
|
|
162
|
+
plugin_name = _default_plugin
|
|
163
|
+
schema_file = parts[0]
|
|
164
|
+
else:
|
|
165
|
+
continue
|
|
166
|
+
script_name = schema_file[:-5] # strip .json → e.g. "orchestrator"
|
|
167
|
+
|
|
168
|
+
if plugin_filter and plugin_name != plugin_filter:
|
|
169
|
+
continue
|
|
170
|
+
|
|
171
|
+
script_path = _find_script(plugin_name, script_name)
|
|
172
|
+
|
|
173
|
+
descriptors.append({
|
|
174
|
+
"plugin": plugin_name,
|
|
175
|
+
"script_name": script_name + ".py",
|
|
176
|
+
"schema_path": schema_path,
|
|
177
|
+
"script_path": script_path,
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
return descriptors
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _find_script(plugin_name: str, script_name: str) -> Path | None:
|
|
184
|
+
"""Locate the target script in the plugin's scripts/ directory.
|
|
185
|
+
|
|
186
|
+
Checks the flat scripts/ directory first, then scripts/v2/, and finally
|
|
187
|
+
searches all subdirectories under scripts/ (e.g. scripts/observability/).
|
|
188
|
+
|
|
189
|
+
Also supports the single-repo layout where this plugin IS the repo root
|
|
190
|
+
(i.e. schemas/wicked-garden/ lives alongside scripts/ at the repo root).
|
|
191
|
+
In that case PLUGINS_ROOT / plugin_name does not exist, so we fall back to
|
|
192
|
+
searching PLUGIN_ROOT / "scripts" directly.
|
|
193
|
+
"""
|
|
194
|
+
target = f"{script_name}.py"
|
|
195
|
+
|
|
196
|
+
# Candidates in priority order: multi-repo layout first, single-repo fallback second.
|
|
197
|
+
scripts_dirs: list[Path] = []
|
|
198
|
+
|
|
199
|
+
external_scripts = PLUGINS_ROOT / plugin_name / "scripts"
|
|
200
|
+
if external_scripts.is_dir():
|
|
201
|
+
scripts_dirs.append(external_scripts)
|
|
202
|
+
|
|
203
|
+
# Single-repo fallback: this plugin's own scripts/ directory (e.g. scripts/observability/)
|
|
204
|
+
own_scripts = PLUGIN_ROOT / "scripts"
|
|
205
|
+
if own_scripts.is_dir() and own_scripts not in scripts_dirs:
|
|
206
|
+
scripts_dirs.append(own_scripts)
|
|
207
|
+
|
|
208
|
+
for scripts_dir in scripts_dirs:
|
|
209
|
+
# Direct match: scripts/{script_name}.py
|
|
210
|
+
candidate = scripts_dir / target
|
|
211
|
+
if candidate.exists():
|
|
212
|
+
return candidate
|
|
213
|
+
|
|
214
|
+
# Also check scripts/v2/ sub-directory (wicked-smaht pattern)
|
|
215
|
+
candidate_v2 = scripts_dir / "v2" / target
|
|
216
|
+
if candidate_v2.exists():
|
|
217
|
+
return candidate_v2
|
|
218
|
+
|
|
219
|
+
# Search all subdirectories under scripts/ (e.g. scripts/observability/)
|
|
220
|
+
matches = sorted(
|
|
221
|
+
m for m in scripts_dir.rglob(target)
|
|
222
|
+
if not any(p.startswith((".", "__")) or p == ".venv"
|
|
223
|
+
for p in m.relative_to(scripts_dir).parts)
|
|
224
|
+
)
|
|
225
|
+
if matches:
|
|
226
|
+
return matches[0]
|
|
227
|
+
|
|
228
|
+
return None
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
# ── Script invocation ────────────────────────────────────────────────────────
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _invoke_script(script_path: Path) -> tuple[str, float]:
|
|
235
|
+
"""
|
|
236
|
+
Run script with --health-check; fall back to bare invocation if that flag
|
|
237
|
+
causes a non-zero exit.
|
|
238
|
+
|
|
239
|
+
Returns (stdout_text, duration_ms).
|
|
240
|
+
Raises subprocess.TimeoutExpired on timeout.
|
|
241
|
+
"""
|
|
242
|
+
start = datetime.now(timezone.utc).timestamp()
|
|
243
|
+
|
|
244
|
+
def _run(extra_args: list[str]) -> subprocess.CompletedProcess:
|
|
245
|
+
env = {**os.environ, "WICKED_TRACE_ACTIVE": "1"}
|
|
246
|
+
return subprocess.run(
|
|
247
|
+
[sys.executable, str(script_path)] + extra_args,
|
|
248
|
+
capture_output=True,
|
|
249
|
+
text=True,
|
|
250
|
+
timeout=TIMEOUT_SECONDS,
|
|
251
|
+
env=env,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
result = _run(["--health-check"])
|
|
255
|
+
|
|
256
|
+
# If health-check flag is unrecognised (argparse exits 2) retry without it
|
|
257
|
+
if result.returncode == 2 and "unrecognized arguments" in result.stderr:
|
|
258
|
+
result = _run([])
|
|
259
|
+
|
|
260
|
+
duration_ms = (datetime.now(timezone.utc).timestamp() - start) * 1000
|
|
261
|
+
return result.stdout or "", round(duration_ms, 1)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
# ── Assertion logic ──────────────────────────────────────────────────────────
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def run_assertion(descriptor: dict) -> dict:
|
|
268
|
+
"""Run one contract assertion. Returns a result record."""
|
|
269
|
+
ts = datetime.now(timezone.utc).isoformat(timespec="milliseconds").replace("+00:00", "Z")
|
|
270
|
+
plugin = descriptor["plugin"]
|
|
271
|
+
script_name = descriptor["script_name"]
|
|
272
|
+
schema_path: Path = descriptor["schema_path"]
|
|
273
|
+
script_path: Path | None = descriptor["script_path"]
|
|
274
|
+
|
|
275
|
+
base = {
|
|
276
|
+
"ts": ts,
|
|
277
|
+
"plugin": plugin,
|
|
278
|
+
"script": script_name,
|
|
279
|
+
"result": "pass",
|
|
280
|
+
"violations": [],
|
|
281
|
+
"duration_ms": 0,
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
# Load schema
|
|
285
|
+
try:
|
|
286
|
+
schema = json.loads(schema_path.read_text(encoding="utf-8"))
|
|
287
|
+
except (OSError, json.JSONDecodeError) as exc:
|
|
288
|
+
base["result"] = "malformed"
|
|
289
|
+
base["violations"] = [{"field": "$", "message": f"could not load schema: {exc}",
|
|
290
|
+
"expected": "valid JSON schema", "actual": "error"}]
|
|
291
|
+
return base
|
|
292
|
+
|
|
293
|
+
if script_path is None:
|
|
294
|
+
base["result"] = "malformed"
|
|
295
|
+
base["violations"] = [{
|
|
296
|
+
"field": "$",
|
|
297
|
+
"message": f"script not found in {plugin}/scripts/",
|
|
298
|
+
"expected": f"{script_name}",
|
|
299
|
+
"actual": "missing",
|
|
300
|
+
}]
|
|
301
|
+
return base
|
|
302
|
+
|
|
303
|
+
# Invoke
|
|
304
|
+
try:
|
|
305
|
+
stdout, duration_ms = _invoke_script(script_path)
|
|
306
|
+
except subprocess.TimeoutExpired:
|
|
307
|
+
base["result"] = "timeout"
|
|
308
|
+
base["duration_ms"] = TIMEOUT_SECONDS * 1000
|
|
309
|
+
return base
|
|
310
|
+
|
|
311
|
+
base["duration_ms"] = round(duration_ms, 1)
|
|
312
|
+
|
|
313
|
+
# Empty check
|
|
314
|
+
if not stdout or not stdout.strip():
|
|
315
|
+
base["result"] = "empty"
|
|
316
|
+
base["violations"] = [{"field": "$", "message": "script produced no output",
|
|
317
|
+
"expected": "non-empty JSON", "actual": "empty"}]
|
|
318
|
+
return base
|
|
319
|
+
|
|
320
|
+
# Parse JSON
|
|
321
|
+
try:
|
|
322
|
+
data = json.loads(stdout.strip())
|
|
323
|
+
except json.JSONDecodeError as exc:
|
|
324
|
+
base["result"] = "malformed"
|
|
325
|
+
base["violations"] = [{"field": "$", "message": f"invalid JSON: {exc}",
|
|
326
|
+
"expected": "valid JSON", "actual": stdout[:200]}]
|
|
327
|
+
return base
|
|
328
|
+
|
|
329
|
+
if data is None or (isinstance(data, (dict, list)) and not data
|
|
330
|
+
and schema.get("type") not in ("array", "null")):
|
|
331
|
+
base["result"] = "empty"
|
|
332
|
+
base["violations"] = [{"field": "$", "message": "script returned empty JSON",
|
|
333
|
+
"expected": "non-empty object", "actual": "empty"}]
|
|
334
|
+
return base
|
|
335
|
+
|
|
336
|
+
# Schema validation
|
|
337
|
+
violations = validate_against_schema(data, schema)
|
|
338
|
+
if violations:
|
|
339
|
+
base["result"] = "malformed"
|
|
340
|
+
base["violations"] = violations
|
|
341
|
+
|
|
342
|
+
return base
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
# ── Output / storage ─────────────────────────────────────────────────────────
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def append_result(record: dict) -> None:
|
|
349
|
+
"""Persist a single assertion result via DomainStore."""
|
|
350
|
+
try:
|
|
351
|
+
_sm.create("assertions", record)
|
|
352
|
+
except Exception as exc:
|
|
353
|
+
print(f"WARNING: Could not persist assertion result: {exc}", file=sys.stderr)
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
# ── Formatting ───────────────────────────────────────────────────────────────
|
|
357
|
+
|
|
358
|
+
_RESULT_ICONS = {
|
|
359
|
+
"pass": "pass",
|
|
360
|
+
"timeout": "TIMEOUT",
|
|
361
|
+
"empty": "EMPTY",
|
|
362
|
+
"malformed": "MALFORMED",
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def _print_human(results: list[dict]) -> None:
|
|
367
|
+
passed = sum(1 for r in results if r["result"] == "pass")
|
|
368
|
+
failed = len(results) - passed
|
|
369
|
+
|
|
370
|
+
for r in results:
|
|
371
|
+
icon = _RESULT_ICONS.get(r["result"], r["result"].upper())
|
|
372
|
+
label = f"[{icon}]"
|
|
373
|
+
line = f" {label:<12} {r['plugin']}/{r['script']} ({r['duration_ms']:.0f}ms)"
|
|
374
|
+
print(line)
|
|
375
|
+
for v in r.get("violations", []):
|
|
376
|
+
print(f" {v['field']}: {v['message']}"
|
|
377
|
+
f" (expected={v['expected']!r}, actual={v['actual']!r})")
|
|
378
|
+
|
|
379
|
+
print()
|
|
380
|
+
print(f"Results: {passed} passed, {failed} failed ({len(results)} total)")
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
# ── Main ─────────────────────────────────────────────────────────────────────
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def main() -> None:
|
|
387
|
+
# Fast-path: --health-check emits a sample assertion result for contract testing
|
|
388
|
+
if "--health-check" in sys.argv:
|
|
389
|
+
print(json.dumps([{
|
|
390
|
+
"ts": datetime.now(timezone.utc).isoformat(),
|
|
391
|
+
"plugin": "health-check",
|
|
392
|
+
"script": "health-check.py",
|
|
393
|
+
"result": "pass",
|
|
394
|
+
"violations": [],
|
|
395
|
+
"duration_ms": 0.0,
|
|
396
|
+
}]))
|
|
397
|
+
return
|
|
398
|
+
|
|
399
|
+
parser = argparse.ArgumentParser(
|
|
400
|
+
description="Contract assertion runner for wicked-garden plugins")
|
|
401
|
+
parser.add_argument("--plugin", help="Only assert contracts for this plugin")
|
|
402
|
+
parser.add_argument("--json", action="store_true", dest="json_output",
|
|
403
|
+
help="Emit JSON array to stdout instead of human-readable text")
|
|
404
|
+
args = parser.parse_args()
|
|
405
|
+
|
|
406
|
+
descriptors = discover_schemas(plugin_filter=args.plugin)
|
|
407
|
+
if not descriptors:
|
|
408
|
+
target = args.plugin or "any plugin"
|
|
409
|
+
msg = f"No schemas found for {target} under {SCHEMAS_DIR}"
|
|
410
|
+
if args.json_output:
|
|
411
|
+
print(json.dumps({"error": msg, "data": [], "meta": {"total": 0}}))
|
|
412
|
+
else:
|
|
413
|
+
print(msg, file=sys.stderr)
|
|
414
|
+
sys.exit(0)
|
|
415
|
+
|
|
416
|
+
results = []
|
|
417
|
+
for descriptor in descriptors:
|
|
418
|
+
record = run_assertion(descriptor)
|
|
419
|
+
append_result(record)
|
|
420
|
+
results.append(record)
|
|
421
|
+
|
|
422
|
+
if args.json_output:
|
|
423
|
+
print(json.dumps(results, indent=2))
|
|
424
|
+
return
|
|
425
|
+
|
|
426
|
+
_print_human(results)
|
|
427
|
+
|
|
428
|
+
# Exit 1 if any failure so CI scripts can detect problems
|
|
429
|
+
if any(r["result"] != "pass" for r in results):
|
|
430
|
+
sys.exit(1)
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
if __name__ == "__main__":
|
|
434
|
+
main()
|