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,737 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ consensus.py — Structured consensus scoring, dissent tracking, and synthesis
4
+ for jam council sessions.
5
+
6
+ Stdlib-only. Cross-platform.
7
+
8
+ CLI:
9
+ consensus.py synthesize --proposals proposals.json [--reviews reviews.json] [--question "..."]
10
+ consensus.py score --proposals proposals.json
11
+ consensus.py format --result result.json [--show-dissent]
12
+ """
13
+ from __future__ import annotations
14
+
15
+ import argparse
16
+ import json
17
+ import os
18
+ import re
19
+ import sys
20
+ from dataclasses import asdict, dataclass, field
21
+ from pathlib import Path
22
+ from typing import Any, Mapping
23
+
24
+ # ---------------------------------------------------------------------------
25
+ # Council raw-vote surfacing (Issue #584)
26
+ #
27
+ # The council agent produces a synthesised verdict from per-model responses,
28
+ # but the individual per-model votes + one-line rationales are never surfaced
29
+ # to the caller. Even on a unanimous verdict, each model may flag a different
30
+ # concern the caller would want to see. ``build_council_output`` assembles an
31
+ # envelope carrying both layers, switched by ``WG_COUNCIL_OUTPUT``.
32
+ # ---------------------------------------------------------------------------
33
+
34
+ #: Maximum characters kept when deriving a per-model rationale from the
35
+ #: model's raw response. Named constant so the value cannot drift between
36
+ #: the extractor and the tests (R3 — no magic values).
37
+ _RATIONALE_MAX_CHARS: int = 240
38
+
39
+ #: Env var controlling which layers ``build_council_output`` emits. Values are
40
+ #: normalised lower-case; unknown values fall back to ``both`` so we never
41
+ #: starve the caller of data because of a typo (graceful degradation).
42
+ ENV_COUNCIL_OUTPUT: str = "WG_COUNCIL_OUTPUT"
43
+ COUNCIL_OUTPUT_BOTH: str = "both"
44
+ COUNCIL_OUTPUT_SYNTH: str = "synth"
45
+ COUNCIL_OUTPUT_RAW: str = "raw"
46
+ _VALID_COUNCIL_OUTPUTS: frozenset[str] = frozenset(
47
+ {COUNCIL_OUTPUT_BOTH, COUNCIL_OUTPUT_SYNTH, COUNCIL_OUTPUT_RAW}
48
+ )
49
+
50
+ #: Label line prefixes a model may emit to mark its own terse summary. When
51
+ #: present, the text following the label wins over the raw-response prefix
52
+ #: so the caller sees the model's own one-liner rather than our slice.
53
+ _SUMMARY_LABEL_PATTERNS: tuple[re.Pattern[str], ...] = (
54
+ re.compile(r"^\s*(?:summary|tl;dr|verdict|recommendation)\s*[:\-]\s*(.+)$", re.IGNORECASE),
55
+ )
56
+
57
+ # ---------------------------------------------------------------------------
58
+ # Import siblings (scripts/ root)
59
+ # ---------------------------------------------------------------------------
60
+ sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
61
+
62
+ from _domain_store import DomainStore # noqa: E402
63
+
64
+
65
+ # ---------------------------------------------------------------------------
66
+ # Data Models
67
+ # ---------------------------------------------------------------------------
68
+
69
+ @dataclass
70
+ class Proposal:
71
+ persona: str
72
+ proposal: str
73
+ rationale: str
74
+ confidence: float # 0.0 to 1.0
75
+ concerns: list[str] = field(default_factory=list)
76
+
77
+
78
+ @dataclass
79
+ class CrossReview:
80
+ reviewer: str
81
+ target_persona: str
82
+ agreements: list[str] = field(default_factory=list)
83
+ disagreements: list[dict] = field(default_factory=list) # [{"point": ..., "counter": ...}]
84
+ questions: list[str] = field(default_factory=list)
85
+
86
+
87
+ @dataclass
88
+ class DissentingView:
89
+ persona: str
90
+ view: str
91
+ strength: str # "strong", "moderate", "mild"
92
+ acknowledged: bool = False
93
+
94
+
95
+ @dataclass
96
+ class ConsensusResult:
97
+ decision: str
98
+ confidence: float
99
+ consensus_points: list[dict] = field(default_factory=list) # [{"point": ..., "agreement": N, "of": M}]
100
+ dissenting_views: list[DissentingView] = field(default_factory=list)
101
+ open_questions: list[str] = field(default_factory=list)
102
+ rounds: int = 1
103
+ participants: int = 0
104
+ proposals: list[Proposal] = field(default_factory=list)
105
+ cross_reviews: list[CrossReview] = field(default_factory=list)
106
+
107
+
108
+ # ---------------------------------------------------------------------------
109
+ # Text helpers
110
+ # ---------------------------------------------------------------------------
111
+
112
+ _MIN_WORD_LEN = 4 # words shorter than this are treated as stop words
113
+
114
+
115
+ def _meaningful_words(text: str) -> set[str]:
116
+ """Extract meaningful words (length >= 4) from text, lowercased."""
117
+ return {
118
+ w for w in re.findall(r"[a-z0-9]+", text.lower())
119
+ if len(w) >= _MIN_WORD_LEN
120
+ }
121
+
122
+
123
+ def _similarity(a: str, b: str) -> float:
124
+ """Word-overlap similarity between two strings (Jaccard on meaningful words)."""
125
+ wa = _meaningful_words(a)
126
+ wb = _meaningful_words(b)
127
+ if not wa or not wb:
128
+ return 0.0
129
+ return len(wa & wb) / len(wa | wb)
130
+
131
+
132
+ def _extract_phrases(text: str) -> list[str]:
133
+ """Split text into key phrases (sentences or bullet points)."""
134
+ # Split on sentence-ending punctuation or newlines with bullets
135
+ parts = re.split(r"(?<=[.!?])\s+|\n+", text.strip())
136
+ # Clean and filter
137
+ phrases = []
138
+ for p in parts:
139
+ cleaned = re.sub(r"^[\s\-*#>]+", "", p).strip()
140
+ if len(cleaned) > 10: # skip trivially short fragments
141
+ phrases.append(cleaned)
142
+ return phrases
143
+
144
+
145
+ # ---------------------------------------------------------------------------
146
+ # Core Functions
147
+ # ---------------------------------------------------------------------------
148
+
149
+ def _find_consensus_points(
150
+ proposals: list[Proposal],
151
+ ) -> tuple[list[dict], list[dict]]:
152
+ """Find consensus and divergent points across proposals.
153
+
154
+ Returns (consensus_points, divergent_points) where each entry is:
155
+ {"point": str, "agreement": int, "of": int}
156
+ """
157
+ n = len(proposals)
158
+ if n == 0:
159
+ return [], []
160
+
161
+ # Gather all phrases from all proposals
162
+ all_phrases: list[tuple[str, int]] = [] # (phrase, proposal_index)
163
+ for i, prop in enumerate(proposals):
164
+ for phrase in _extract_phrases(prop.proposal):
165
+ all_phrases.append((phrase, i))
166
+
167
+ # Deduplicate similar phrases, keeping the longest representative
168
+ clusters: list[dict] = [] # {"representative": str, "sources": set[int]}
169
+ for phrase, idx in all_phrases:
170
+ merged = False
171
+ for cluster in clusters:
172
+ if _similarity(phrase, cluster["representative"]) > 0.35:
173
+ cluster["sources"].add(idx)
174
+ # Keep longer phrase as representative
175
+ if len(phrase) > len(cluster["representative"]):
176
+ cluster["representative"] = phrase
177
+ merged = True
178
+ break
179
+ if not merged:
180
+ clusters.append({"representative": phrase, "sources": {idx}})
181
+
182
+ consensus = []
183
+ divergent = []
184
+ for cluster in clusters:
185
+ count = len(cluster["sources"])
186
+ entry = {
187
+ "point": cluster["representative"],
188
+ "agreement": count,
189
+ "of": n,
190
+ }
191
+ ratio = count / n
192
+ if ratio >= 0.6:
193
+ consensus.append(entry)
194
+ elif ratio < 0.4:
195
+ divergent.append(entry)
196
+
197
+ # Sort by agreement descending
198
+ consensus.sort(key=lambda x: x["agreement"], reverse=True)
199
+ divergent.sort(key=lambda x: x["agreement"])
200
+ return consensus, divergent
201
+
202
+
203
+ def score_consensus(proposals: list[Proposal]) -> dict:
204
+ """Calculate consensus metrics from independent proposals.
205
+
206
+ Returns:
207
+ {
208
+ "consensus_points": [...],
209
+ "confidence": float,
210
+ "agreement_ratio": float,
211
+ "divergent_points": [...],
212
+ }
213
+ """
214
+ consensus_pts, divergent_pts = _find_consensus_points(proposals)
215
+
216
+ # Average confidence across proposals
217
+ avg_confidence = (
218
+ sum(p.confidence for p in proposals) / len(proposals)
219
+ if proposals else 0.0
220
+ )
221
+
222
+ # Agreement ratio: proportion of points that are consensus vs total
223
+ total_pts = len(consensus_pts) + len(divergent_pts)
224
+ agreement_ratio = (
225
+ len(consensus_pts) / total_pts if total_pts > 0 else 0.0
226
+ )
227
+
228
+ return {
229
+ "consensus_points": consensus_pts,
230
+ "confidence": round(avg_confidence, 3),
231
+ "agreement_ratio": round(agreement_ratio, 3),
232
+ "divergent_points": divergent_pts,
233
+ }
234
+
235
+
236
+ def extract_dissent(
237
+ proposals: list[Proposal],
238
+ cross_reviews: list[CrossReview] | None = None,
239
+ ) -> list[DissentingView]:
240
+ """Extract dissenting views from proposals and cross-reviews.
241
+
242
+ Strength classification:
243
+ - "strong": raised by 2+ personas or has detailed counter-rationale
244
+ - "moderate": raised by 1 persona with rationale
245
+ - "mild": noted as concern without strong objection
246
+ """
247
+ dissents: list[DissentingView] = []
248
+ concern_tracker: dict[str, list[str]] = {} # concern_text -> [personas]
249
+
250
+ # Stage 1: Gather concerns from proposals
251
+ for prop in proposals:
252
+ for concern in prop.concerns:
253
+ key = concern.strip().lower()
254
+ if key not in concern_tracker:
255
+ concern_tracker[key] = []
256
+ concern_tracker[key].append(prop.persona)
257
+
258
+ for concern_text, personas in concern_tracker.items():
259
+ # Find the original-case version
260
+ original = concern_text
261
+ for prop in proposals:
262
+ for c in prop.concerns:
263
+ if c.strip().lower() == concern_text:
264
+ original = c.strip()
265
+ break
266
+
267
+ if len(personas) >= 2:
268
+ strength = "strong"
269
+ else:
270
+ strength = "moderate"
271
+
272
+ dissents.append(DissentingView(
273
+ persona=", ".join(sorted(set(personas))),
274
+ view=original,
275
+ strength=strength,
276
+ acknowledged=False,
277
+ ))
278
+
279
+ # Stage 2: Extract from cross-reviews
280
+ if cross_reviews:
281
+ for review in cross_reviews:
282
+ for disagreement in review.disagreements:
283
+ point = disagreement.get("point", "")
284
+ counter = disagreement.get("counter", "")
285
+ if not point:
286
+ continue
287
+
288
+ # Check if already captured as a concern
289
+ already_captured = any(
290
+ _similarity(point, d.view) > 0.5 for d in dissents
291
+ )
292
+ if already_captured:
293
+ # Upgrade to strong if it appears in cross-review too
294
+ for d in dissents:
295
+ if _similarity(point, d.view) > 0.5:
296
+ d.strength = "strong"
297
+ break
298
+ continue
299
+
300
+ # Detailed counter-rationale makes it strong
301
+ strength = "strong" if len(counter) > 50 else "moderate"
302
+ dissents.append(DissentingView(
303
+ persona=review.reviewer,
304
+ view=point,
305
+ strength=strength,
306
+ acknowledged=False,
307
+ ))
308
+
309
+ # Unresolved questions are mild dissent
310
+ for question in review.questions:
311
+ if len(question.strip()) > 10:
312
+ dissents.append(DissentingView(
313
+ persona=review.reviewer,
314
+ view=question.strip(),
315
+ strength="mild",
316
+ acknowledged=False,
317
+ ))
318
+
319
+ return dissents
320
+
321
+
322
+ def synthesize(
323
+ proposals: list[Proposal],
324
+ cross_reviews: list[CrossReview] | None = None,
325
+ question: str = "",
326
+ ) -> ConsensusResult:
327
+ """Run the full 3-stage consensus protocol.
328
+
329
+ Stage 1: Analyze independent proposals
330
+ Stage 2: Process cross-reviews (if provided)
331
+ Stage 3: Produce structured synthesis
332
+ """
333
+ # Stage 1: Score consensus from proposals
334
+ scores = score_consensus(proposals)
335
+
336
+ # Stage 2: Extract dissent (includes cross-review processing)
337
+ dissents = extract_dissent(proposals, cross_reviews)
338
+
339
+ # Stage 3: Synthesize decision
340
+ consensus_pts = scores["consensus_points"]
341
+
342
+ # Build decision summary from top consensus points
343
+ if consensus_pts:
344
+ top_points = [cp["point"] for cp in consensus_pts[:5]]
345
+ decision = ". ".join(top_points)
346
+ if not decision.endswith("."):
347
+ decision += "."
348
+ elif proposals:
349
+ # No clear consensus — use highest-confidence proposal
350
+ best = max(proposals, key=lambda p: p.confidence)
351
+ decision = best.proposal
352
+ else:
353
+ decision = "No proposals provided."
354
+
355
+ # Collect open questions from cross-reviews
356
+ open_questions: list[str] = []
357
+ if cross_reviews:
358
+ for review in cross_reviews:
359
+ open_questions.extend(review.questions)
360
+ # Deduplicate
361
+ seen: set[str] = set()
362
+ unique_questions: list[str] = []
363
+ for q in open_questions:
364
+ q_lower = q.strip().lower()
365
+ if q_lower and q_lower not in seen:
366
+ seen.add(q_lower)
367
+ unique_questions.append(q.strip())
368
+
369
+ return ConsensusResult(
370
+ decision=decision,
371
+ confidence=scores["confidence"],
372
+ consensus_points=consensus_pts,
373
+ dissenting_views=dissents,
374
+ open_questions=unique_questions,
375
+ rounds=1 if not cross_reviews else 2,
376
+ participants=len(proposals),
377
+ proposals=proposals,
378
+ cross_reviews=cross_reviews or [],
379
+ )
380
+
381
+
382
+ # ---------------------------------------------------------------------------
383
+ # Formatting
384
+ # ---------------------------------------------------------------------------
385
+
386
+ def format_for_memory(result: ConsensusResult) -> dict:
387
+ """Format consensus result for storage in wicked-brain:memory.
388
+
389
+ Returns dict suitable for memory.create() with:
390
+ - content: decision summary
391
+ - type: "decision"
392
+ - metadata: consensus details including dissent
393
+ """
394
+ strong_dissents = [
395
+ d for d in result.dissenting_views if d.strength == "strong"
396
+ ]
397
+ return {
398
+ "content": result.decision,
399
+ "type": "decision",
400
+ "metadata": {
401
+ "confidence": result.confidence,
402
+ "participants": result.participants,
403
+ "rounds": result.rounds,
404
+ "consensus_point_count": len(result.consensus_points),
405
+ "dissent_count": len(result.dissenting_views),
406
+ "strong_dissent_count": len(strong_dissents),
407
+ "strong_dissents": [
408
+ {"persona": d.persona, "view": d.view}
409
+ for d in strong_dissents
410
+ ],
411
+ "open_question_count": len(result.open_questions),
412
+ },
413
+ }
414
+
415
+
416
+ def format_for_display(
417
+ result: ConsensusResult, show_dissent: bool = False,
418
+ ) -> str:
419
+ """Format consensus result as human-readable markdown."""
420
+ lines: list[str] = []
421
+ lines.append("## Council Consensus")
422
+ lines.append("")
423
+ lines.append(f"**Confidence:** {result.confidence:.0%}")
424
+ lines.append(f"**Participants:** {result.participants}")
425
+ lines.append(f"**Rounds:** {result.rounds}")
426
+ lines.append("")
427
+
428
+ lines.append("### Decision")
429
+ lines.append("")
430
+ lines.append(result.decision)
431
+ lines.append("")
432
+
433
+ if result.consensus_points:
434
+ lines.append("### Consensus Points")
435
+ lines.append("")
436
+ for cp in result.consensus_points:
437
+ lines.append(
438
+ f"- ({cp['agreement']}/{cp['of']}) {cp['point']}"
439
+ )
440
+ lines.append("")
441
+
442
+ if show_dissent and result.dissenting_views:
443
+ lines.append("### Dissenting Views")
444
+ lines.append("")
445
+ for dv in result.dissenting_views:
446
+ icon = {"strong": "!!", "moderate": "!", "mild": "~"}.get(
447
+ dv.strength, "~"
448
+ )
449
+ lines.append(f"- [{icon}] **{dv.persona}**: {dv.view}")
450
+ lines.append("")
451
+
452
+ if result.open_questions:
453
+ lines.append("### Open Questions")
454
+ lines.append("")
455
+ for q in result.open_questions:
456
+ lines.append(f"- {q}")
457
+ lines.append("")
458
+
459
+ return "\n".join(lines)
460
+
461
+
462
+ # ---------------------------------------------------------------------------
463
+ # Storage
464
+ # ---------------------------------------------------------------------------
465
+
466
+ def store_council_result(result: ConsensusResult, session_id: str) -> dict:
467
+ """Store council result via DomainStore("wicked-jam").
468
+
469
+ Stores under source "council-results" with full consensus data.
470
+ """
471
+ ds = DomainStore("wicked-jam")
472
+ payload = {
473
+ "session_id": session_id,
474
+ "decision": result.decision,
475
+ "confidence": result.confidence,
476
+ "consensus_points": result.consensus_points,
477
+ "dissenting_views": [asdict(d) for d in result.dissenting_views],
478
+ "open_questions": result.open_questions,
479
+ "rounds": result.rounds,
480
+ "participants": result.participants,
481
+ "proposals": [asdict(p) for p in result.proposals],
482
+ "cross_reviews": [asdict(r) for r in result.cross_reviews],
483
+ }
484
+ return ds.create("council-results", payload)
485
+
486
+
487
+ # ---------------------------------------------------------------------------
488
+ # Deserialization helpers
489
+ # ---------------------------------------------------------------------------
490
+
491
+ def _proposals_from_json(data: list[dict]) -> list[Proposal]:
492
+ return [
493
+ Proposal(
494
+ persona=d["persona"],
495
+ proposal=d["proposal"],
496
+ rationale=d.get("rationale", ""),
497
+ confidence=float(d.get("confidence", 0.5)),
498
+ concerns=d.get("concerns", []),
499
+ )
500
+ for d in data
501
+ ]
502
+
503
+
504
+ def _reviews_from_json(data: list[dict]) -> list[CrossReview]:
505
+ return [
506
+ CrossReview(
507
+ reviewer=d["reviewer"],
508
+ target_persona=d["target_persona"],
509
+ agreements=d.get("agreements", []),
510
+ disagreements=d.get("disagreements", []),
511
+ questions=d.get("questions", []),
512
+ )
513
+ for d in data
514
+ ]
515
+
516
+
517
+ def _result_from_json(data: dict) -> ConsensusResult:
518
+ return ConsensusResult(
519
+ decision=data["decision"],
520
+ confidence=float(data["confidence"]),
521
+ consensus_points=data.get("consensus_points", []),
522
+ dissenting_views=[
523
+ DissentingView(**dv)
524
+ for dv in data.get("dissenting_views", [])
525
+ ],
526
+ open_questions=data.get("open_questions", []),
527
+ rounds=data.get("rounds", 1),
528
+ participants=data.get("participants", 0),
529
+ proposals=_proposals_from_json(data.get("proposals", [])),
530
+ cross_reviews=_reviews_from_json(data.get("cross_reviews", [])),
531
+ )
532
+
533
+
534
+ # ---------------------------------------------------------------------------
535
+ # Council raw-vote surfacing (Issue #584)
536
+ # ---------------------------------------------------------------------------
537
+
538
+ def _extract_rationale(
539
+ raw_text: str,
540
+ max_chars: int = _RATIONALE_MAX_CHARS,
541
+ ) -> str:
542
+ """Derive a one-line rationale from a model's raw response (Issue #584).
543
+
544
+ Selection rules (deterministic — same input always yields same output):
545
+
546
+ 1. If the response contains a ``summary:`` / ``tl;dr:`` / ``verdict:`` /
547
+ ``recommendation:`` label line, take the text after the first such
548
+ label. Models frequently emit their own terse one-liner we should
549
+ prefer over our own slice.
550
+ 2. Otherwise take the first ``max_chars`` characters of the trimmed
551
+ response, collapsing internal whitespace to single spaces so the
552
+ rationale fits on one line.
553
+
554
+ The returned string is always ``<= max_chars`` characters.
555
+ """
556
+ if not raw_text:
557
+ return ""
558
+
559
+ text = raw_text.strip()
560
+ # Rule 1: look for a model-authored summary label on any line.
561
+ for line in text.splitlines():
562
+ for pattern in _SUMMARY_LABEL_PATTERNS:
563
+ match = pattern.match(line)
564
+ if match:
565
+ candidate = re.sub(r"\s+", " ", match.group(1)).strip()
566
+ if candidate:
567
+ return candidate[:max_chars]
568
+
569
+ # Rule 2: fall back to a prefix of the flattened response.
570
+ flattened = re.sub(r"\s+", " ", text).strip()
571
+ return flattened[:max_chars]
572
+
573
+
574
+ def _normalise_confidence(value: Any) -> float | None:
575
+ """Return a float confidence in [0.0, 1.0] or ``None``.
576
+
577
+ ``None``, missing keys, empty strings, and unparseable values all map to
578
+ ``None`` — critically, they must NOT collapse to ``0.0`` because that
579
+ would be indistinguishable from a model that explicitly voted
580
+ low-confidence (Issue #584 — missing != zero).
581
+ """
582
+ if value is None:
583
+ return None
584
+ if isinstance(value, bool):
585
+ # bool is a subclass of int in Python; reject it to avoid True -> 1.0.
586
+ return None
587
+ try:
588
+ conf = float(value)
589
+ except (TypeError, ValueError):
590
+ return None
591
+ # Clamp into [0.0, 1.0] — models occasionally emit 0-100 scales; treating
592
+ # out-of-range as None keeps the output honest rather than silently wrong.
593
+ if conf < 0.0 or conf > 1.0:
594
+ return None
595
+ return conf
596
+
597
+
598
+ def _build_raw_vote(vote: Mapping[str, Any]) -> dict[str, Any]:
599
+ """Build a single raw-vote entry from an input vote record.
600
+
601
+ Input schema (tolerant — any of these fields may be present):
602
+ {
603
+ "model": str, # required
604
+ "verdict": str, # required (passthrough)
605
+ "confidence": float | None, # optional
606
+ "rationale": str, # optional (caller-supplied)
607
+ "raw_text": str, # optional (model response)
608
+ "response": str, # optional alias for raw_text
609
+ }
610
+
611
+ Output schema (stable — this is the public surface per Issue #584):
612
+ {"model", "verdict", "confidence", "rationale"}
613
+ """
614
+ # Model + verdict are passed through as strings; ``None`` is preserved as
615
+ # ``None`` so downstream consumers can distinguish "no vote" from "empty".
616
+ model = vote.get("model")
617
+ verdict = vote.get("verdict")
618
+
619
+ # Rationale precedence: caller-supplied > model-extracted > empty string.
620
+ supplied = vote.get("rationale")
621
+ if isinstance(supplied, str) and supplied.strip():
622
+ rationale = re.sub(r"\s+", " ", supplied.strip())[:_RATIONALE_MAX_CHARS]
623
+ else:
624
+ raw_text = vote.get("raw_text") or vote.get("response") or ""
625
+ rationale = _extract_rationale(str(raw_text))
626
+
627
+ return {
628
+ "model": model,
629
+ "verdict": verdict,
630
+ "confidence": _normalise_confidence(vote.get("confidence")),
631
+ "rationale": rationale,
632
+ }
633
+
634
+
635
+ def _read_council_output_mode(env: Mapping[str, str] | None) -> str:
636
+ """Read ``WG_COUNCIL_OUTPUT`` and return a validated mode string."""
637
+ source = env if env is not None else os.environ
638
+ raw = (source.get(ENV_COUNCIL_OUTPUT) or COUNCIL_OUTPUT_BOTH).strip().lower()
639
+ if raw not in _VALID_COUNCIL_OUTPUTS:
640
+ return COUNCIL_OUTPUT_BOTH
641
+ return raw
642
+
643
+
644
+ def build_council_output(
645
+ votes: list[Mapping[str, Any]],
646
+ synthesized: Mapping[str, Any] | None,
647
+ *,
648
+ env: Mapping[str, str] | None = None,
649
+ ) -> dict[str, Any]:
650
+ """Assemble the council output envelope (Issue #584).
651
+
652
+ Emits the synthesised verdict and/or the raw per-model votes based on the
653
+ ``WG_COUNCIL_OUTPUT`` env var (default ``both``):
654
+
655
+ * ``both`` — top-level ``synthesized`` + ``raw_votes``
656
+ * ``synth`` — ``synthesized`` only (legacy shape; backward-compatible)
657
+ * ``raw`` — ``raw_votes`` only (unvarnished per-model layer)
658
+
659
+ Args:
660
+ votes: One dict per model that voted. Each dict carries at least a
661
+ ``model`` and ``verdict``; ``confidence``, ``rationale``, and
662
+ ``raw_text`` are optional. See :func:`_build_raw_vote`.
663
+ synthesized: The synthesised verdict shape already produced by the
664
+ council agent. Passed through unchanged; this function does not
665
+ rewrite which models were called or how votes were counted.
666
+ env: Optional env mapping (testing seam; defaults to ``os.environ``).
667
+
668
+ Returns:
669
+ Dict with the requested top-level keys. Callers depending on the
670
+ legacy single-key shape should set ``WG_COUNCIL_OUTPUT=synth``.
671
+ """
672
+ mode = _read_council_output_mode(env)
673
+ raw_votes = [_build_raw_vote(v) for v in votes]
674
+ synth_payload: dict[str, Any] = dict(synthesized) if synthesized else {}
675
+
676
+ output: dict[str, Any] = {}
677
+ if mode in (COUNCIL_OUTPUT_BOTH, COUNCIL_OUTPUT_SYNTH):
678
+ output["synthesized"] = synth_payload
679
+ if mode in (COUNCIL_OUTPUT_BOTH, COUNCIL_OUTPUT_RAW):
680
+ output["raw_votes"] = raw_votes
681
+ return output
682
+
683
+
684
+ # ---------------------------------------------------------------------------
685
+ # CLI
686
+ # ---------------------------------------------------------------------------
687
+
688
+ def main() -> None:
689
+ parser = argparse.ArgumentParser(
690
+ description="Jam council consensus scoring and synthesis",
691
+ )
692
+ sub = parser.add_subparsers(dest="command", required=True)
693
+
694
+ # synthesize
695
+ syn = sub.add_parser("synthesize", help="Run full consensus protocol")
696
+ syn.add_argument("--proposals", required=True, help="Path to proposals JSON")
697
+ syn.add_argument("--reviews", default=None, help="Path to cross-reviews JSON")
698
+ syn.add_argument("--question", default="", help="Original question")
699
+
700
+ # score
701
+ sc = sub.add_parser("score", help="Score consensus from proposals")
702
+ sc.add_argument("--proposals", required=True, help="Path to proposals JSON")
703
+
704
+ # format
705
+ fmt = sub.add_parser("format", help="Format a consensus result")
706
+ fmt.add_argument("--result", required=True, help="Path to result JSON")
707
+ fmt.add_argument("--show-dissent", action="store_true", help="Show dissenting views")
708
+
709
+ args = parser.parse_args()
710
+
711
+ if args.command == "synthesize":
712
+ proposals = _proposals_from_json(
713
+ json.loads(Path(args.proposals).read_text(encoding="utf-8"))
714
+ )
715
+ reviews = None
716
+ if args.reviews:
717
+ reviews = _reviews_from_json(
718
+ json.loads(Path(args.reviews).read_text(encoding="utf-8"))
719
+ )
720
+ result = synthesize(proposals, reviews, args.question)
721
+ sys.stdout.write(json.dumps(asdict(result), indent=2))
722
+
723
+ elif args.command == "score":
724
+ proposals = _proposals_from_json(
725
+ json.loads(Path(args.proposals).read_text(encoding="utf-8"))
726
+ )
727
+ scores = score_consensus(proposals)
728
+ sys.stdout.write(json.dumps(scores, indent=2))
729
+
730
+ elif args.command == "format":
731
+ data = json.loads(Path(args.result).read_text(encoding="utf-8"))
732
+ result = _result_from_json(data)
733
+ sys.stdout.write(format_for_display(result, show_dissent=args.show_dissent))
734
+
735
+
736
+ if __name__ == "__main__":
737
+ main()