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.
Files changed (640) hide show
  1. package/.claude-plugin/archetypes.json +166 -0
  2. package/.claude-plugin/components.json +75 -0
  3. package/.claude-plugin/marketplace.json +15 -0
  4. package/.claude-plugin/plugin.json +174 -0
  5. package/.claude-plugin/specialist.json +48 -0
  6. package/.claude-plugin/stack-registry.json +96 -0
  7. package/AGENTS.md +13 -0
  8. package/CHANGELOG.md +712 -0
  9. package/CONTRIBUTING.md +99 -0
  10. package/ETHOS.md +83 -0
  11. package/LICENSE +21 -0
  12. package/README.md +115 -0
  13. package/WICKED_GARDEN_BUS_EVENTS.md +149 -0
  14. package/agents/agentic/architect.md +449 -0
  15. package/agents/agentic/performance-analyst.md +702 -0
  16. package/agents/agentic/safety-reviewer.md +662 -0
  17. package/agents/crew/implementer.md +163 -0
  18. package/agents/crew/researcher.md +94 -0
  19. package/agents/crew/reviewer.md +184 -0
  20. package/agents/data/data-engineer.md +227 -0
  21. package/agents/engineering/api-documentarian.md +440 -0
  22. package/agents/engineering/migration-engineer.md +301 -0
  23. package/agents/engineering/solution-architect.md +208 -0
  24. package/agents/jam/brainstorm-facilitator.md +297 -0
  25. package/agents/jam/council.md +322 -0
  26. package/agents/jam/quick-facilitator.md +85 -0
  27. package/agents/persona/persona-agent.md +121 -0
  28. package/agents/platform/compliance-officer.md +266 -0
  29. package/agents/platform/privacy-expert.md +496 -0
  30. package/agents/platform/security-engineer.md +287 -0
  31. package/agents/product/a11y-expert.md +323 -0
  32. package/agents/product/requirements-analyst.md +238 -0
  33. package/agents/product/ui-reviewer.md +180 -0
  34. package/agents/product/ux-designer.md +199 -0
  35. package/agents/product/value-strategist.md +269 -0
  36. package/agents/qe/semantic-reviewer.md +78 -0
  37. package/commands/agentic/audit.md +24 -0
  38. package/commands/agentic/design.md +24 -0
  39. package/commands/agentic/frameworks.md +25 -0
  40. package/commands/agentic/review.md +27 -0
  41. package/commands/archetype/build.md +12 -0
  42. package/commands/archetype/decide.md +12 -0
  43. package/commands/archetype/explore.md +12 -0
  44. package/commands/archetype/incident.md +12 -0
  45. package/commands/archetype/migrate.md +12 -0
  46. package/commands/archetype/modernize.md +12 -0
  47. package/commands/archetype/review.md +14 -0
  48. package/commands/archetype/ship.md +12 -0
  49. package/commands/archetype/specify.md +12 -0
  50. package/commands/archetype/triage.md +12 -0
  51. package/commands/compile.md +37 -0
  52. package/commands/data/analyze.md +18 -0
  53. package/commands/data/data.md +21 -0
  54. package/commands/data/ml.md +18 -0
  55. package/commands/data/ontology.md +22 -0
  56. package/commands/data/pipeline.md +17 -0
  57. package/commands/deliberate.md +181 -0
  58. package/commands/engineering/add-field.md +77 -0
  59. package/commands/engineering/apply.md +58 -0
  60. package/commands/engineering/arch.md +26 -0
  61. package/commands/engineering/debug.md +27 -0
  62. package/commands/engineering/docs.md +27 -0
  63. package/commands/engineering/new-generator.md +257 -0
  64. package/commands/engineering/patch-plan.md +59 -0
  65. package/commands/engineering/plan.md +29 -0
  66. package/commands/engineering/remove.md +48 -0
  67. package/commands/engineering/rename.md +44 -0
  68. package/commands/engineering/review.md +32 -0
  69. package/commands/help.md +128 -0
  70. package/commands/install.md +118 -0
  71. package/commands/intent.md +54 -0
  72. package/commands/jam/brainstorm.md +37 -0
  73. package/commands/jam/council.md +49 -0
  74. package/commands/jam/quick.md +21 -0
  75. package/commands/jam/revisit.md +79 -0
  76. package/commands/persona/as.md +136 -0
  77. package/commands/persona/define.md +24 -0
  78. package/commands/persona/list.md +118 -0
  79. package/commands/platform/actions.md +20 -0
  80. package/commands/platform/assert.md +18 -0
  81. package/commands/platform/audit.md +23 -0
  82. package/commands/platform/compliance.md +20 -0
  83. package/commands/platform/gh.md +22 -0
  84. package/commands/platform/health.md +22 -0
  85. package/commands/platform/incident.md +23 -0
  86. package/commands/platform/infra.md +24 -0
  87. package/commands/platform/peer-health.md +103 -0
  88. package/commands/platform/plugin-health.md +58 -0
  89. package/commands/platform/security.md +38 -0
  90. package/commands/platform/toolchain.md +96 -0
  91. package/commands/platform/traces.md +20 -0
  92. package/commands/product/a11y.md +19 -0
  93. package/commands/product/acceptance.md +19 -0
  94. package/commands/product/align.md +20 -0
  95. package/commands/product/analyze.md +23 -0
  96. package/commands/product/elicit.md +19 -0
  97. package/commands/product/listen.md +24 -0
  98. package/commands/product/mockup.md +20 -0
  99. package/commands/product/screenshot.md +19 -0
  100. package/commands/product/strategy.md +40 -0
  101. package/commands/product/synthesize.md +23 -0
  102. package/commands/product/ux-review.md +42 -0
  103. package/commands/product/ux.md +20 -0
  104. package/commands/product/visual-direction.md +34 -0
  105. package/commands/prove.md +47 -0
  106. package/commands/report-issue.md +243 -0
  107. package/commands/reset.md +144 -0
  108. package/commands/search/blast-radius.md +58 -0
  109. package/commands/search/hotspots.md +55 -0
  110. package/commands/search/index.md +38 -0
  111. package/commands/search/lineage.md +47 -0
  112. package/commands/search/service-map.md +79 -0
  113. package/commands/setup.md +363 -0
  114. package/commands/smaht/briefing.md +200 -0
  115. package/commands/smaht/events-import.md +95 -0
  116. package/commands/smaht/state.md +19 -0
  117. package/commands/where-am-i.md +58 -0
  118. package/docs/README.md +32 -0
  119. package/docs/adr/0001-code-relationship-graph-engine.md +116 -0
  120. package/docs/adr/0002-major-cleanup-keep-cut.md +91 -0
  121. package/docs/adr/0003-peers-gate-required-layers-optin.md +41 -0
  122. package/docs/adr/0004-code-graph-moves-to-wicked-brain.md +47 -0
  123. package/docs/brain-chunk-format.md +135 -0
  124. package/docs/compiler.md +95 -0
  125. package/docs/daemon-api.md +70 -0
  126. package/docs/domains.md +197 -0
  127. package/docs/getting-started.md +208 -0
  128. package/docs/plans/2026-06-08-wicked-loom-conduct.md +1337 -0
  129. package/docs/plans/2026-06-08-wicked-loom-contract.md +638 -0
  130. package/docs/plans/2026-06-08-wicked-loom-cutover.md +1300 -0
  131. package/docs/plans/2026-06-08-wicked-loom-expand-compose.md +843 -0
  132. package/docs/plans/2026-06-08-wicked-loom-flow-cutover.md +403 -0
  133. package/docs/plans/2026-06-10-codegraph-to-brain-phase1a-graph-core.md +838 -0
  134. package/docs/plans/2026-06-10-codegraph-to-brain-phase1b-injected-edges.md +44 -0
  135. package/docs/required-peers.md +60 -0
  136. package/docs/specs/2026-06-08-wicked-loom-extraction.md +225 -0
  137. package/docs/specs/2026-06-10-codegraph-to-brain-migration.md +168 -0
  138. package/docs/v11/archetypes.md +231 -0
  139. package/hooks/hooks.json +275 -0
  140. package/hooks/scripts/bootstrap.py +1797 -0
  141. package/hooks/scripts/invoke.py +62 -0
  142. package/hooks/scripts/notification.py +166 -0
  143. package/hooks/scripts/permission_request.py +223 -0
  144. package/hooks/scripts/post_tool.py +1374 -0
  145. package/hooks/scripts/pre_compact.py +272 -0
  146. package/hooks/scripts/pre_tool.py +1278 -0
  147. package/hooks/scripts/prompt_submit.py +1109 -0
  148. package/hooks/scripts/session_end.py +95 -0
  149. package/hooks/scripts/stop.py +624 -0
  150. package/hooks/scripts/subagent_lifecycle.py +464 -0
  151. package/hooks/scripts/subscribers/on_gate_decided.py +84 -0
  152. package/hooks/scripts/task_completed.py +322 -0
  153. package/hooks/scripts/worktree_guard.py +148 -0
  154. package/hooks/subscriptions/on_gate_decided.json +9 -0
  155. package/install.mjs +128 -0
  156. package/package.json +41 -0
  157. package/pyproject.toml +59 -0
  158. package/schemas/README.md +32 -0
  159. package/schemas/assert_contracts.json +37 -0
  160. package/schemas/health_probe.json +46 -0
  161. package/schemas/plugin_status.json +37 -0
  162. package/scripts/_adapters/__init__.py +166 -0
  163. package/scripts/_adapters/jira_adapter.py +18 -0
  164. package/scripts/_adapters/linear_adapter.py +18 -0
  165. package/scripts/_adapters/miro_adapter.py +18 -0
  166. package/scripts/_adapters/notion_adapter.py +18 -0
  167. package/scripts/_agents.py +357 -0
  168. package/scripts/_brain_ingest/__init__.py +2 -0
  169. package/scripts/_brain_ingest/session_fact_extractor.py +341 -0
  170. package/scripts/_brain_port.py +163 -0
  171. package/scripts/_bus.py +1081 -0
  172. package/scripts/_bus_catalog_gen.py +93 -0
  173. package/scripts/_bus_consumers.json +46 -0
  174. package/scripts/_bus_emit.py +43 -0
  175. package/scripts/_capability_registry.py +334 -0
  176. package/scripts/_capability_resolver.py +322 -0
  177. package/scripts/_domain_store.py +559 -0
  178. package/scripts/_event_log_reader.py +154 -0
  179. package/scripts/_event_schema.py +305 -0
  180. package/scripts/_event_store.py +397 -0
  181. package/scripts/_heavy_cadence.py +324 -0
  182. package/scripts/_integration_resolver.py +321 -0
  183. package/scripts/_logger.py +151 -0
  184. package/scripts/_loom.py +482 -0
  185. package/scripts/_paths.py +133 -0
  186. package/scripts/_python.sh +22 -0
  187. package/scripts/_run.py +137 -0
  188. package/scripts/_session.py +502 -0
  189. package/scripts/_validate_registry.py +1042 -0
  190. package/scripts/_wicked_testing_probe.py +277 -0
  191. package/scripts/agentic/analyze_agents.py +391 -0
  192. package/scripts/agentic/detect_framework.py +420 -0
  193. package/scripts/agentic/issue_taxonomy.py +357 -0
  194. package/scripts/agentic/pattern_scorer.py +434 -0
  195. package/scripts/classify/persist.py +169 -0
  196. package/scripts/compiler/compile.py +552 -0
  197. package/scripts/compiler/phase0/VERDICT.md +60 -0
  198. package/scripts/compiler/phase0/detect.py +292 -0
  199. package/scripts/compiler/phase0/emit.py +228 -0
  200. package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.bindings.json +30 -0
  201. package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.lint-output.txt +17 -0
  202. package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/.wg/evidence/failing-tests.txt +4 -0
  203. package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/.wg/evidence/honest-tests.txt +2 -0
  204. package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/fab-fail.build.md +8 -0
  205. package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/fab-missing.build.md +8 -0
  206. package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/honest.build.md +8 -0
  207. package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/wg_check_claims.py +174 -0
  208. package/scripts/compiler/phase0/evidence/command_iq.bindings.json +45 -0
  209. package/scripts/compiler/phase0/evidence/command_iq.lint-output.txt +17 -0
  210. package/scripts/compiler/phase0/evidence/command_iq.stage/.projects/evidence-domain/phase-1-clarify/evidence/failing-tests.txt +4 -0
  211. package/scripts/compiler/phase0/evidence/command_iq.stage/.projects/evidence-domain/phase-1-clarify/evidence/honest-tests.txt +3 -0
  212. package/scripts/compiler/phase0/evidence/command_iq.stage/fab-fail.build.md +8 -0
  213. package/scripts/compiler/phase0/evidence/command_iq.stage/fab-missing.build.md +8 -0
  214. package/scripts/compiler/phase0/evidence/command_iq.stage/honest.build.md +8 -0
  215. package/scripts/compiler/phase0/evidence/command_iq.stage/wg_check_claims.py +174 -0
  216. package/scripts/compiler/phase0/evidence/memos.bindings.json +28 -0
  217. package/scripts/compiler/phase0/evidence/memos.lint-output.txt +17 -0
  218. package/scripts/compiler/phase0/evidence/memos.stage/.wg/evidence/failing-tests.txt +4 -0
  219. package/scripts/compiler/phase0/evidence/memos.stage/.wg/evidence/honest-tests.txt +2 -0
  220. package/scripts/compiler/phase0/evidence/memos.stage/fab-fail.build.md +8 -0
  221. package/scripts/compiler/phase0/evidence/memos.stage/fab-missing.build.md +8 -0
  222. package/scripts/compiler/phase0/evidence/memos.stage/honest.build.md +8 -0
  223. package/scripts/compiler/phase0/evidence/memos.stage/wg_check_claims.py +174 -0
  224. package/scripts/compiler/phase0/evidence/verdict.json +104 -0
  225. package/scripts/compiler/phase0/evidence/wicked-bus.bindings.json +29 -0
  226. package/scripts/compiler/phase0/evidence/wicked-bus.lint-output.txt +17 -0
  227. package/scripts/compiler/phase0/evidence/wicked-bus.stage/coverage/failing-tests.txt +4 -0
  228. package/scripts/compiler/phase0/evidence/wicked-bus.stage/coverage/honest-tests.txt +3 -0
  229. package/scripts/compiler/phase0/evidence/wicked-bus.stage/fab-fail.build.md +8 -0
  230. package/scripts/compiler/phase0/evidence/wicked-bus.stage/fab-missing.build.md +8 -0
  231. package/scripts/compiler/phase0/evidence/wicked-bus.stage/honest.build.md +8 -0
  232. package/scripts/compiler/phase0/evidence/wicked-bus.stage/wg_check_claims.py +174 -0
  233. package/scripts/compiler/phase0/evidence/wire-vault-transcript.txt +60 -0
  234. package/scripts/compiler/phase0/run_probe.py +234 -0
  235. package/scripts/compiler/phase0/wire_vault.py +310 -0
  236. package/scripts/crew/__init__.py +0 -0
  237. package/scripts/crew/_task_audit_writer.py +243 -0
  238. package/scripts/crew/_task_reader.py +389 -0
  239. package/scripts/crew/archetypes_v11.py +348 -0
  240. package/scripts/crew/detectors/__init__.py +12 -0
  241. package/scripts/crew/flow_compiler.py +121 -0
  242. package/scripts/crew/phase_manager.py +703 -0
  243. package/scripts/crew/scope_delta.py +264 -0
  244. package/scripts/crew/specialist_resolver.py +218 -0
  245. package/scripts/crew/stack_registry.py +189 -0
  246. package/scripts/data/data_profiler.py +271 -0
  247. package/scripts/data/ontology_recommender.py +231 -0
  248. package/scripts/data/schema_validator.py +267 -0
  249. package/scripts/delivery/drift.py +739 -0
  250. package/scripts/delivery/telemetry.py +614 -0
  251. package/scripts/engineering/patch/codegraph_db.py +125 -0
  252. package/scripts/engineering/patch/generators/__init__.py +56 -0
  253. package/scripts/engineering/patch/generators/base.py +373 -0
  254. package/scripts/engineering/patch/generators/csharp_generator.py +494 -0
  255. package/scripts/engineering/patch/generators/go_generator.py +430 -0
  256. package/scripts/engineering/patch/generators/java_generator.py +928 -0
  257. package/scripts/engineering/patch/generators/jsp_generator.py +454 -0
  258. package/scripts/engineering/patch/generators/kotlin_generator.py +561 -0
  259. package/scripts/engineering/patch/generators/perl_generator.py +510 -0
  260. package/scripts/engineering/patch/generators/php_generator.py +644 -0
  261. package/scripts/engineering/patch/generators/propagation_engine.py +929 -0
  262. package/scripts/engineering/patch/generators/python_generator.py +487 -0
  263. package/scripts/engineering/patch/generators/ruby_generator.py +463 -0
  264. package/scripts/engineering/patch/generators/rust_generator.py +400 -0
  265. package/scripts/engineering/patch/generators/sql_generator.py +594 -0
  266. package/scripts/engineering/patch/generators/typescript_generator.py +439 -0
  267. package/scripts/engineering/patch/patch.py +800 -0
  268. package/scripts/engineering/patch/safety.py +429 -0
  269. package/scripts/jam/__init__.py +0 -0
  270. package/scripts/jam/_bus_consumers.py +271 -0
  271. package/scripts/jam/agentic_cli_registry.py +703 -0
  272. package/scripts/jam/consensus.py +737 -0
  273. package/scripts/jam/detect_clis.py +409 -0
  274. package/scripts/jam/jam.py +283 -0
  275. package/scripts/jam/save_transcript.py +171 -0
  276. package/scripts/loom/__init__.py +22 -0
  277. package/scripts/loom/cli.py +170 -0
  278. package/scripts/loom/compose.py +180 -0
  279. package/scripts/loom/gate.py +120 -0
  280. package/scripts/loom/manifest.py +140 -0
  281. package/scripts/loom/resolve.py +59 -0
  282. package/scripts/persona/__init__.py +0 -0
  283. package/scripts/persona/registry.py +688 -0
  284. package/scripts/platform/guard_pipeline.py +1214 -0
  285. package/scripts/platform/guard_profiles.py +216 -0
  286. package/scripts/platform/observability/assert_contracts.py +434 -0
  287. package/scripts/platform/observability/health_probe.py +915 -0
  288. package/scripts/platform/observability/ops_log_viewer.py +209 -0
  289. package/scripts/platform/prereq_doctor.py +567 -0
  290. package/scripts/qe/conditions_manifest.py +260 -0
  291. package/scripts/qe/content_sanitizer.py +160 -0
  292. package/scripts/qe/coverage_tracker.py +535 -0
  293. package/scripts/qe/discover_scenarios.py +243 -0
  294. package/scripts/qe/evidence_tracker.py +232 -0
  295. package/scripts/qe/prove.py +196 -0
  296. package/scripts/qe/registry_lookup.py +80 -0
  297. package/scripts/qe/registry_store.py +176 -0
  298. package/scripts/qe/semantic_review.py +1005 -0
  299. package/scripts/qe/vault_gate.py +382 -0
  300. package/scripts/qe/verdict_audit.py +175 -0
  301. package/scripts/qe/verdict_schema.py +335 -0
  302. package/scripts/reset.py +220 -0
  303. package/scripts/resolve_path.py +22 -0
  304. package/scripts/sentinel/invariants.py +402 -0
  305. package/scripts/sentinel/pre_push.py +73 -0
  306. package/scripts/setup/__init__.py +1 -0
  307. package/scripts/setup/detect_state.py +114 -0
  308. package/scripts/setup/migrate_legacy.py +67 -0
  309. package/scripts/setup/migrate_v6_projects.py +232 -0
  310. package/scripts/setup/onboarding.py +114 -0
  311. package/scripts/smaht/_bus_consumers.py +98 -0
  312. package/scripts/smaht/adapters/__init__.py +83 -0
  313. package/scripts/smaht/adapters/brain_adapter.py +254 -0
  314. package/scripts/smaht/adapters/context7_adapter.py +438 -0
  315. package/scripts/smaht/adapters/delegation_adapter.py +104 -0
  316. package/scripts/smaht/adapters/domain_adapter.py +206 -0
  317. package/scripts/smaht/adapters/events_adapter.py +231 -0
  318. package/scripts/smaht/adapters/tools_adapter.py +49 -0
  319. package/scripts/smaht/cheatsheet_store.py +236 -0
  320. package/scripts/smaht/context_package.py +386 -0
  321. package/scripts/smaht/knowledge_graph.py +395 -0
  322. package/scripts/smaht/propose_skills.py +856 -0
  323. package/scripts/smaht/test_cheatsheet_store.py +428 -0
  324. package/scripts/statusline.py +151 -0
  325. package/scripts/where_am_i.py +236 -0
  326. package/skills/agentic/agentic-patterns/SKILL.md +91 -0
  327. package/skills/agentic/agentic-patterns/refs/anti-patterns-design.md +283 -0
  328. package/skills/agentic/agentic-patterns/refs/anti-patterns-operational.md +216 -0
  329. package/skills/agentic/agentic-patterns/refs/pattern-catalog.md +329 -0
  330. package/skills/agentic/context-engineering/SKILL.md +142 -0
  331. package/skills/agentic/context-engineering/refs/caching-and-optimization.md +251 -0
  332. package/skills/agentic/context-engineering/refs/compression-techniques.md +171 -0
  333. package/skills/agentic/context-engineering/refs/cost-calculation-budget.md +250 -0
  334. package/skills/agentic/context-engineering/refs/cost-optimization-reporting.md +300 -0
  335. package/skills/agentic/context-engineering/refs/selective-loading.md +179 -0
  336. package/skills/agentic/frameworks/SKILL.md +191 -0
  337. package/skills/agentic/frameworks/refs/framework-profiles-1.md +209 -0
  338. package/skills/agentic/frameworks/refs/framework-profiles-2.md +214 -0
  339. package/skills/agentic/frameworks/refs/migration-patterns-paths.md +267 -0
  340. package/skills/agentic/frameworks/refs/migration-patterns-testing.md +208 -0
  341. package/skills/agentic/refs/audit.md +134 -0
  342. package/skills/agentic/refs/design.md +137 -0
  343. package/skills/agentic/refs/frameworks.md +102 -0
  344. package/skills/agentic/refs/review.md +160 -0
  345. package/skills/agentic/review-methodology/SKILL.md +186 -0
  346. package/skills/agentic/review-methodology/refs/deliverable-templates.md +246 -0
  347. package/skills/agentic/review-methodology/refs/issue-taxonomy-quality-testing.md +277 -0
  348. package/skills/agentic/review-methodology/refs/issue-taxonomy-reliability-safety.md +253 -0
  349. package/skills/agentic/trust-and-safety/SKILL.md +177 -0
  350. package/skills/agentic/trust-and-safety/refs/guardrails-actions.md +246 -0
  351. package/skills/agentic/trust-and-safety/refs/guardrails-input-output.md +238 -0
  352. package/skills/agentic/trust-and-safety/refs/guardrails-resources.md +209 -0
  353. package/skills/agentic/trust-and-safety/refs/safety-checklist-advanced.md +209 -0
  354. package/skills/agentic/trust-and-safety/refs/safety-checklist-core.md +215 -0
  355. package/skills/archetype/SKILL.md +104 -0
  356. package/skills/archetype/refs/build.md +142 -0
  357. package/skills/archetype/refs/decide.md +117 -0
  358. package/skills/archetype/refs/explore.md +66 -0
  359. package/skills/archetype/refs/incident.md +149 -0
  360. package/skills/archetype/refs/migrate.md +170 -0
  361. package/skills/archetype/refs/modernize.md +187 -0
  362. package/skills/archetype/refs/review.md +145 -0
  363. package/skills/archetype/refs/ship.md +117 -0
  364. package/skills/archetype/refs/specify.md +108 -0
  365. package/skills/archetype/refs/triage.md +62 -0
  366. package/skills/classify/SKILL.md +157 -0
  367. package/skills/data/analysis/SKILL.md +125 -0
  368. package/skills/data/analysis/refs/templates.md +170 -0
  369. package/skills/data/data/SKILL.md +118 -0
  370. package/skills/data/data/refs/examples.md +235 -0
  371. package/skills/data/ml/SKILL.md +125 -0
  372. package/skills/data/ml/refs/templates.md +172 -0
  373. package/skills/data/pipeline/SKILL.md +136 -0
  374. package/skills/data/pipeline/refs/templates.md +175 -0
  375. package/skills/data/refs/analyze.md +123 -0
  376. package/skills/data/refs/data.md +95 -0
  377. package/skills/data/refs/ml.md +118 -0
  378. package/skills/data/refs/pipeline.md +92 -0
  379. package/skills/deliberate/SKILL.md +125 -0
  380. package/skills/deliberate/refs/architecture-lens.md +37 -0
  381. package/skills/deliberate/refs/assumptions-lens.md +43 -0
  382. package/skills/deliberate/refs/code-lens.md +38 -0
  383. package/skills/deliberate/refs/content-lens.md +37 -0
  384. package/skills/deliberate/refs/data-lens.md +43 -0
  385. package/skills/deliberate/refs/design-lens.md +37 -0
  386. package/skills/deliberate/refs/opportunity-patterns.md +107 -0
  387. package/skills/deliberate/refs/ops-lens.md +43 -0
  388. package/skills/deliberate/refs/product-lens.md +46 -0
  389. package/skills/deliberate/refs/rethink-framework.md +77 -0
  390. package/skills/deliberate/refs/security-lens.md +47 -0
  391. package/skills/deliberate/refs/testing-lens.md +45 -0
  392. package/skills/deliberate/refs/timing-lens.md +42 -0
  393. package/skills/engineering/architecture/SKILL.md +177 -0
  394. package/skills/engineering/architecture/refs/adr-template.md +268 -0
  395. package/skills/engineering/architecture/refs/architecture-template-deployment.md +176 -0
  396. package/skills/engineering/architecture/refs/architecture-template-design.md +300 -0
  397. package/skills/engineering/architecture/refs/examples-ecommerce-analytics.md +299 -0
  398. package/skills/engineering/architecture/refs/examples-saas-trading.md +291 -0
  399. package/skills/engineering/architecture/refs/patterns-advanced.md +238 -0
  400. package/skills/engineering/architecture/refs/patterns-structural.md +303 -0
  401. package/skills/engineering/backend/SKILL.md +102 -0
  402. package/skills/engineering/backend/refs/checklists.md +146 -0
  403. package/skills/engineering/backend/refs/patterns.md +181 -0
  404. package/skills/engineering/debugging/SKILL.md +99 -0
  405. package/skills/engineering/debugging/refs/patterns.md +248 -0
  406. package/skills/engineering/debugging/refs/process.md +203 -0
  407. package/skills/engineering/docs/SKILL.md +97 -0
  408. package/skills/engineering/engineering/SKILL.md +88 -0
  409. package/skills/engineering/engineering/refs/checklists.md +91 -0
  410. package/skills/engineering/engineering/refs/templates.md +71 -0
  411. package/skills/engineering/frontend/SKILL.md +92 -0
  412. package/skills/engineering/frontend/refs/checklists.md +96 -0
  413. package/skills/engineering/frontend/refs/patterns.md +167 -0
  414. package/skills/engineering/integration/SKILL.md +183 -0
  415. package/skills/engineering/integration/refs/error-handling-best-practices.md +139 -0
  416. package/skills/engineering/integration/refs/error-handling-error-classes-typescript.md +212 -0
  417. package/skills/engineering/integration/refs/error-handling-error-response-format.md +301 -0
  418. package/skills/engineering/integration/refs/error-handling-retry-strategies.md +190 -0
  419. package/skills/engineering/integration/refs/event-schemas-best-practices.md +225 -0
  420. package/skills/engineering/integration/refs/event-schemas-event-naming-conventions.md +303 -0
  421. package/skills/engineering/integration/refs/event-schemas-event-structure.md +288 -0
  422. package/skills/engineering/integration/refs/graphql-best-practices.md +138 -0
  423. package/skills/engineering/integration/refs/graphql-error-handling.md +198 -0
  424. package/skills/engineering/integration/refs/graphql-graphql-basics.md +197 -0
  425. package/skills/engineering/integration/refs/graphql-query-examples.md +226 -0
  426. package/skills/engineering/integration/refs/graphql-schema-design-patterns.md +280 -0
  427. package/skills/engineering/integration/refs/grpc-grpc-overview.md +246 -0
  428. package/skills/engineering/integration/refs/grpc-implementation-examples.md +269 -0
  429. package/skills/engineering/integration/refs/grpc-metadata-headers.md +304 -0
  430. package/skills/engineering/integration/refs/grpc-testing.md +49 -0
  431. package/skills/engineering/integration/refs/openapi-template.yaml +682 -0
  432. package/skills/engineering/integration/refs/resilience-best-practices.md +109 -0
  433. package/skills/engineering/integration/refs/resilience-circuit-breaker-pattern.md +302 -0
  434. package/skills/engineering/integration/refs/resilience-rate-limiting.md +245 -0
  435. package/skills/engineering/integration/refs/resilience-timeout-pattern.md +212 -0
  436. package/skills/engineering/integration/refs/rest-api-fundamentals.md +299 -0
  437. package/skills/engineering/integration/refs/rest-api-responses-security.md +302 -0
  438. package/skills/engineering/integration/refs/versioning-breaking-changes.md +237 -0
  439. package/skills/engineering/integration/refs/versioning-migration-strategies.md +257 -0
  440. package/skills/engineering/integration/refs/versioning-versioning-strategies.md +296 -0
  441. package/skills/engineering/large-scale-migration/SKILL.md +65 -0
  442. package/skills/engineering/patch/SKILL.md +184 -0
  443. package/skills/engineering/refs/arch.md +126 -0
  444. package/skills/engineering/refs/debug.md +142 -0
  445. package/skills/engineering/refs/docs.md +190 -0
  446. package/skills/engineering/refs/plan.md +109 -0
  447. package/skills/engineering/refs/review.md +138 -0
  448. package/skills/engineering/system-design/SKILL.md +154 -0
  449. package/skills/engineering/system-design/refs/anti-patterns-big-ball-of-mud.md +254 -0
  450. package/skills/engineering/system-design/refs/anti-patterns-circular-dependencies.md +241 -0
  451. package/skills/engineering/system-design/refs/anti-patterns-feature-envy.md +168 -0
  452. package/skills/engineering/system-design/refs/anti-patterns-god-object.md +247 -0
  453. package/skills/engineering/system-design/refs/component-template-lifecycle.md +210 -0
  454. package/skills/engineering/system-design/refs/component-template-maintenance.md +219 -0
  455. package/skills/engineering/system-design/refs/component-template-structure.md +276 -0
  456. package/skills/engineering/system-design/refs/dependencies-best-practices.md +260 -0
  457. package/skills/engineering/system-design/refs/dependencies-dependency-principles.md +295 -0
  458. package/skills/engineering/system-design/refs/dependencies-dependency-tracking.md +261 -0
  459. package/skills/engineering/system-design/refs/interface-template-lifecycle.md +211 -0
  460. package/skills/engineering/system-design/refs/interface-template-maintenance.md +212 -0
  461. package/skills/engineering/system-design/refs/interface-template-structure.md +269 -0
  462. package/skills/engineering/system-design/refs/patterns-dependency-injection.md +151 -0
  463. package/skills/engineering/system-design/refs/patterns-facade-pattern.md +257 -0
  464. package/skills/engineering/system-design/refs/patterns-layered-pattern.md +204 -0
  465. package/skills/engineering/system-design/refs/patterns-plugin-architecture.md +229 -0
  466. package/skills/engineering/unit-test-quality/SKILL.md +194 -0
  467. package/skills/ground/SKILL.md +130 -0
  468. package/skills/integration-discovery/SKILL.md +168 -0
  469. package/skills/integration-discovery/refs/capabilities.md +282 -0
  470. package/skills/integration-discovery/refs/cli-detection.md +295 -0
  471. package/skills/integration-discovery/refs/discovery-sources.md +233 -0
  472. package/skills/integration-discovery/refs/task-patterns.md +206 -0
  473. package/skills/jam/SKILL.md +45 -0
  474. package/skills/jam/refs/facilitation-patterns.md +125 -0
  475. package/skills/jam/refs/quick.md +64 -0
  476. package/skills/jam/refs/synthesis-patterns.md +114 -0
  477. package/skills/multi-model/SKILL.md +168 -0
  478. package/skills/multi-model/refs/aichat.md +119 -0
  479. package/skills/multi-model/refs/aider.md +100 -0
  480. package/skills/multi-model/refs/auditability.md +238 -0
  481. package/skills/multi-model/refs/codex.md +169 -0
  482. package/skills/multi-model/refs/context.md +306 -0
  483. package/skills/multi-model/refs/copilot.md +161 -0
  484. package/skills/multi-model/refs/examples.md +224 -0
  485. package/skills/multi-model/refs/gemini.md +185 -0
  486. package/skills/multi-model/refs/goose.md +112 -0
  487. package/skills/multi-model/refs/llm.md +124 -0
  488. package/skills/multi-model/refs/opencode.md +196 -0
  489. package/skills/multi-model/refs/orchestration.md +188 -0
  490. package/skills/multi-model/refs/pi.md +208 -0
  491. package/skills/persona/SKILL.md +144 -0
  492. package/skills/persona/refs/define.md +133 -0
  493. package/skills/platform/actions/refs/actions.md +114 -0
  494. package/skills/platform/audit/SKILL.md +174 -0
  495. package/skills/platform/audit/refs/audit.md +145 -0
  496. package/skills/platform/audit/refs/checklists-evidence-operations.md +144 -0
  497. package/skills/platform/audit/refs/checklists-gdpr-pci-evidence.md +213 -0
  498. package/skills/platform/audit/refs/checklists-soc2-hipaa.md +120 -0
  499. package/skills/platform/audit/refs/frameworks-gdpr-pci.md +225 -0
  500. package/skills/platform/audit/refs/frameworks-soc2-hipaa.md +228 -0
  501. package/skills/platform/compliance/SKILL.md +169 -0
  502. package/skills/platform/compliance/refs/checklists.md +303 -0
  503. package/skills/platform/compliance/refs/compliance.md +117 -0
  504. package/skills/platform/compliance/refs/frameworks.md +282 -0
  505. package/skills/platform/errors/SKILL.md +183 -0
  506. package/skills/platform/errors/refs/patterns.md +271 -0
  507. package/skills/platform/errors/refs/severity.md +264 -0
  508. package/skills/platform/gate-benchmark-rebaseline/SKILL.md +108 -0
  509. package/skills/platform/gh/refs/gh.md +105 -0
  510. package/skills/platform/gh-cli/SKILL.md +120 -0
  511. package/skills/platform/gh-cli/refs/automation.md +63 -0
  512. package/skills/platform/gh-cli/refs/gh-ops.md +103 -0
  513. package/skills/platform/gh-cli/refs/patterns.md +132 -0
  514. package/skills/platform/gh-cli/scripts/gh_ops.py +454 -0
  515. package/skills/platform/github-actions/SKILL.md +159 -0
  516. package/skills/platform/github-actions/refs/security.md +150 -0
  517. package/skills/platform/github-actions/refs/templates.md +230 -0
  518. package/skills/platform/github-actions/refs/troubleshooting.md +174 -0
  519. package/skills/platform/github-actions/scripts/workflow_gen.py +215 -0
  520. package/skills/platform/gitlab-ci/SKILL.md +140 -0
  521. package/skills/platform/gitlab-ci/refs/templates.md +223 -0
  522. package/skills/platform/gitlab-ci/refs/troubleshooting.md +213 -0
  523. package/skills/platform/glab-cli/SKILL.md +133 -0
  524. package/skills/platform/glab-cli/refs/ci-debugging.md +95 -0
  525. package/skills/platform/glab-cli/refs/glab-ops.md +78 -0
  526. package/skills/platform/glab-cli/refs/patterns.md +131 -0
  527. package/skills/platform/glab-cli/scripts/glab_ops.py +313 -0
  528. package/skills/platform/health/SKILL.md +177 -0
  529. package/skills/platform/health/refs/health.md +72 -0
  530. package/skills/platform/health/refs/sources.md +302 -0
  531. package/skills/platform/incident/refs/incident.md +136 -0
  532. package/skills/platform/infra/refs/infra.md +104 -0
  533. package/skills/platform/observability/SKILL.md +128 -0
  534. package/skills/platform/observability/refs/toolchain-discovery.md +243 -0
  535. package/skills/platform/prereq-doctor/SKILL.md +57 -0
  536. package/skills/platform/prereq-doctor/refs/tool-registry.md +58 -0
  537. package/skills/platform/traces/refs/traces.md +101 -0
  538. package/skills/product/acceptance-criteria/SKILL.md +193 -0
  539. package/skills/product/acceptance-criteria/refs/ac-anti-patterns.md +291 -0
  540. package/skills/product/acceptance-criteria/refs/ac-examples.md +277 -0
  541. package/skills/product/accessibility/SKILL.md +197 -0
  542. package/skills/product/accessibility/refs/aria-patterns-basics.md +250 -0
  543. package/skills/product/accessibility/refs/aria-patterns-dynamic.md +187 -0
  544. package/skills/product/accessibility/refs/aria-patterns-interactive.md +246 -0
  545. package/skills/product/accessibility/refs/audit-report-findings.md +248 -0
  546. package/skills/product/accessibility/refs/audit-report-results.md +276 -0
  547. package/skills/product/accessibility/refs/common-violations-html-css.md +270 -0
  548. package/skills/product/accessibility/refs/common-violations-interactive.md +303 -0
  549. package/skills/product/accessibility/refs/keyboard-testing-basics.md +222 -0
  550. package/skills/product/accessibility/refs/keyboard-testing-patterns.md +173 -0
  551. package/skills/product/accessibility/refs/screen-reader-testing-advanced.md +294 -0
  552. package/skills/product/accessibility/refs/screen-reader-testing-commands.md +255 -0
  553. package/skills/product/accessibility/refs/wcag-checklist.md +262 -0
  554. package/skills/product/analyze/SKILL.md +164 -0
  555. package/skills/product/analyze/refs/algorithms.md +238 -0
  556. package/skills/product/analyze/refs/sentiment-patterns.md +180 -0
  557. package/skills/product/imagery/SKILL.md +97 -0
  558. package/skills/product/imagery/alter/SKILL.md +105 -0
  559. package/skills/product/imagery/alter/refs/editing-reference.md +146 -0
  560. package/skills/product/imagery/create/SKILL.md +109 -0
  561. package/skills/product/imagery/create/refs/prompt-engineering.md +112 -0
  562. package/skills/product/imagery/create/refs/provider-reference.md +219 -0
  563. package/skills/product/imagery/review/SKILL.md +101 -0
  564. package/skills/product/imagery/review/refs/analysis-lenses.md +136 -0
  565. package/skills/product/imagery/review/refs/quality-gates.md +153 -0
  566. package/skills/product/imagery/scripts/analyze.sh +23 -0
  567. package/skills/product/imagery/scripts/edit.sh +14 -0
  568. package/skills/product/imagery/scripts/generate.sh +13 -0
  569. package/skills/product/imagery/scripts/provider.py +787 -0
  570. package/skills/product/listen/SKILL.md +175 -0
  571. package/skills/product/listen/refs/channels.md +220 -0
  572. package/skills/product/mockup/SKILL.md +106 -0
  573. package/skills/product/mockup/refs/templates.md +95 -0
  574. package/skills/product/refs/a11y.md +109 -0
  575. package/skills/product/refs/acceptance.md +79 -0
  576. package/skills/product/refs/align.md +67 -0
  577. package/skills/product/refs/analyze.md +77 -0
  578. package/skills/product/refs/elicit.md +67 -0
  579. package/skills/product/refs/listen.md +58 -0
  580. package/skills/product/refs/mockup.md +77 -0
  581. package/skills/product/refs/screenshot.md +59 -0
  582. package/skills/product/refs/strategy.md +77 -0
  583. package/skills/product/refs/synthesize.md +54 -0
  584. package/skills/product/refs/ux-review.md +68 -0
  585. package/skills/product/refs/ux.md +101 -0
  586. package/skills/product/requirements-analysis/SKILL.md +136 -0
  587. package/skills/product/requirements-analysis/refs/requirements-example-auth.md +264 -0
  588. package/skills/product/requirements-analysis/refs/requirements-example-export-integration.md +88 -0
  589. package/skills/product/requirements-analysis/refs/requirements-output-format-template.md +247 -0
  590. package/skills/product/requirements-analysis/refs/user-story-guide.md +296 -0
  591. package/skills/product/requirements-graph/SKILL.md +155 -0
  592. package/skills/product/requirements-graph/refs/examples.md +223 -0
  593. package/skills/product/requirements-graph/refs/schema.md +162 -0
  594. package/skills/product/requirements-migrate/SKILL.md +90 -0
  595. package/skills/product/requirements-navigate/SKILL.md +141 -0
  596. package/skills/product/requirements-navigate/refs/meta-templates.md +141 -0
  597. package/skills/product/screenshot/SKILL.md +147 -0
  598. package/skills/product/strategy/SKILL.md +198 -0
  599. package/skills/product/strategy/refs/frameworks.md +295 -0
  600. package/skills/product/strategy/refs/templates-competitive-market.md +299 -0
  601. package/skills/product/strategy/refs/templates-roi-value.md +207 -0
  602. package/skills/product/synthesize/SKILL.md +116 -0
  603. package/skills/product/synthesize/refs/journey-mapping.md +212 -0
  604. package/skills/product/synthesize/refs/prioritization.md +193 -0
  605. package/skills/product/ux-review/SKILL.md +164 -0
  606. package/skills/product/visual-direction/SKILL.md +137 -0
  607. package/skills/product/visual-review/SKILL.md +131 -0
  608. package/skills/runtime-exec/SKILL.md +138 -0
  609. package/skills/search/codebase-narrator/SKILL.md +131 -0
  610. package/skills/search/codebase-narrator/refs/output-template.md +100 -0
  611. package/skills/smaht/SKILL.md +72 -0
  612. package/skills/smaht/discovery/SKILL.md +65 -0
  613. package/skills/smaht/intent/SKILL.md +89 -0
  614. package/skills/smaht/propose-skills/SKILL.md +142 -0
  615. package/skills/smaht/refs/state.md +76 -0
  616. package/skills/swarm/SKILL.md +132 -0
  617. package/skills/swarm/refs/fan-out.md +104 -0
  618. package/skills/swarm/refs/independent-verification.md +86 -0
  619. package/skills/swarm/refs/receipts-and-evidence.md +69 -0
  620. package/skills/swarm/refs/recon-synthesis.md +87 -0
  621. package/skills/swarm/refs/ship-discipline.md +90 -0
  622. package/skills/wickedizer/SKILL.md +147 -0
  623. package/skills/wickedizer/refs/artifact-types.md +238 -0
  624. package/skills/wickedizer/refs/complexity.md +278 -0
  625. package/skills/wickedizer/refs/de-ai-pass.md +140 -0
  626. package/skills/wickedizer/refs/failure-modes.md +249 -0
  627. package/skills/wickedizer/refs/patterns.md +282 -0
  628. package/skills/wickedizer/refs/review.md +221 -0
  629. package/skills/wickedizer/refs/structure.md +282 -0
  630. package/skills/wickedizer/refs/voice-profile.md +125 -0
  631. package/skills/workflow/SKILL.md +132 -0
  632. package/skills/workflow/refs/archetype-adjustments.md +205 -0
  633. package/skills/workflow/refs/evidence.md +58 -0
  634. package/skills/workflow/refs/integration.md +130 -0
  635. package/skills/workflow/refs/risk-dimension-signals.md +260 -0
  636. package/skills/workflow/refs/scoring-rubric.md +49 -0
  637. package/skills/workflow/refs/signal-keywords.md +176 -0
  638. package/skills/workflow/refs/specialist-routing-rules.md +159 -0
  639. package/skills/worktrees/SKILL.md +87 -0
  640. 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())