@litfamily/lithermes 1.0.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 (557) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +186 -0
  3. package/README_Ko-KR.md +188 -0
  4. package/assets/lithermes-plugin/NOTICE.md +46 -0
  5. package/assets/lithermes-plugin/README.md +390 -0
  6. package/assets/lithermes-plugin/__init__.py +589 -0
  7. package/assets/lithermes-plugin/auto_update.py +338 -0
  8. package/assets/lithermes-plugin/bounded_work.py +1120 -0
  9. package/assets/lithermes-plugin/bounded_work_tools.py +115 -0
  10. package/assets/lithermes-plugin/core.py +1813 -0
  11. package/assets/lithermes-plugin/core_commands.py +243 -0
  12. package/assets/lithermes-plugin/core_contexts.py +458 -0
  13. package/assets/lithermes-plugin/core_contract.py +456 -0
  14. package/assets/lithermes-plugin/core_plans.py +418 -0
  15. package/assets/lithermes-plugin/core_reader_args.py +34 -0
  16. package/assets/lithermes-plugin/core_recap_command.py +141 -0
  17. package/assets/lithermes-plugin/core_recap_state.py +175 -0
  18. package/assets/lithermes-plugin/core_review.py +198 -0
  19. package/assets/lithermes-plugin/core_routing.py +641 -0
  20. package/assets/lithermes-plugin/core_runs.py +283 -0
  21. package/assets/lithermes-plugin/core_runtime.py +251 -0
  22. package/assets/lithermes-plugin/core_workflow_commands.py +207 -0
  23. package/assets/lithermes-plugin/deliverable_hedge_guard.py +562 -0
  24. package/assets/lithermes-plugin/diagnostics.py +468 -0
  25. package/assets/lithermes-plugin/handoff.py +97 -0
  26. package/assets/lithermes-plugin/knowledge.py +920 -0
  27. package/assets/lithermes-plugin/knowledge_tools.py +130 -0
  28. package/assets/lithermes-plugin/lit_mark.py +140 -0
  29. package/assets/lithermes-plugin/lit_mark_rows.json +1363 -0
  30. package/assets/lithermes-plugin/litgoal/__init__.py +10 -0
  31. package/assets/lithermes-plugin/litgoal/cli.py +207 -0
  32. package/assets/lithermes-plugin/litgoal/hook.py +81 -0
  33. package/assets/lithermes-plugin/litgoal/model.py +203 -0
  34. package/assets/lithermes-plugin/litgoal/runtime.py +522 -0
  35. package/assets/lithermes-plugin/litgoal/store.py +367 -0
  36. package/assets/lithermes-plugin/litgoal/tools.py +300 -0
  37. package/assets/lithermes-plugin/output-styles/asd-ste100-ko.md +41 -0
  38. package/assets/lithermes-plugin/output-styles/asd-ste100.md +40 -0
  39. package/assets/lithermes-plugin/output-styles/eli5-ko.md +13 -0
  40. package/assets/lithermes-plugin/output-styles/eli5.md +11 -0
  41. package/assets/lithermes-plugin/output_styles.py +62 -0
  42. package/assets/lithermes-plugin/payload-version.json +2107 -0
  43. package/assets/lithermes-plugin/plugin.yaml +16 -0
  44. package/assets/lithermes-plugin/provider_metrics.py +161 -0
  45. package/assets/lithermes-plugin/redaction.py +148 -0
  46. package/assets/lithermes-plugin/rules/__init__.py +55 -0
  47. package/assets/lithermes-plugin/rules/bundled-rules/baseline-discipline.md +20 -0
  48. package/assets/lithermes-plugin/rules/bundled-rules/build-decision-gate.md +19 -0
  49. package/assets/lithermes-plugin/rules/bundled-rules/windows-git-bash.md +17 -0
  50. package/assets/lithermes-plugin/rules/constants.py +122 -0
  51. package/assets/lithermes-plugin/rules/discovery.py +276 -0
  52. package/assets/lithermes-plugin/rules/engine.py +447 -0
  53. package/assets/lithermes-plugin/rules/frontmatter.py +149 -0
  54. package/assets/lithermes-plugin/rules/globmatch.py +333 -0
  55. package/assets/lithermes-plugin/scientific_visualization.py +158 -0
  56. package/assets/lithermes-plugin/session_context.py +8 -0
  57. package/assets/lithermes-plugin/skill_loop.py +2044 -0
  58. package/assets/lithermes-plugin/skill_observer.py +1778 -0
  59. package/assets/lithermes-plugin/skills/autoconference/LICENSE +21 -0
  60. package/assets/lithermes-plugin/skills/autoconference/ORIGIN.json +11 -0
  61. package/assets/lithermes-plugin/skills/autoconference/SKILL.md +163 -0
  62. package/assets/lithermes-plugin/skills/autoconference/assets/conference_template.md +76 -0
  63. package/assets/lithermes-plugin/skills/autoconference/assets/report_template.md +53 -0
  64. package/assets/lithermes-plugin/skills/autoconference/assets/synthesis_template.md +39 -0
  65. package/assets/lithermes-plugin/skills/autoconference/dependencies/autoresearch.md +14 -0
  66. package/assets/lithermes-plugin/skills/autoconference/modes/analyze/SKILL.md +269 -0
  67. package/assets/lithermes-plugin/skills/autoconference/modes/core/SKILL.md +134 -0
  68. package/assets/lithermes-plugin/skills/autoconference/modes/core/convergence-guide.md +162 -0
  69. package/assets/lithermes-plugin/skills/autoconference/modes/core/crash-recovery.md +63 -0
  70. package/assets/lithermes-plugin/skills/autoconference/modes/debate/SKILL.md +383 -0
  71. package/assets/lithermes-plugin/skills/autoconference/modes/plan/SKILL.md +304 -0
  72. package/assets/lithermes-plugin/skills/autoconference/modes/resume/SKILL.md +82 -0
  73. package/assets/lithermes-plugin/skills/autoconference/modes/ship/SKILL.md +78 -0
  74. package/assets/lithermes-plugin/skills/autoconference/modes/survey/SKILL.md +376 -0
  75. package/assets/lithermes-plugin/skills/autoconference/references/agent-prompts.md +110 -0
  76. package/assets/lithermes-plugin/skills/autoconference/references/conference-protocol.md +90 -0
  77. package/assets/lithermes-plugin/skills/autoconference/references/core-principles.md +78 -0
  78. package/assets/lithermes-plugin/skills/autoconference/references/results-logging.md +165 -0
  79. package/assets/lithermes-plugin/skills/autoconference/references/visualization-guide.md +99 -0
  80. package/assets/lithermes-plugin/skills/autoconference/scripts/check_conference.sh +197 -0
  81. package/assets/lithermes-plugin/skills/autoconference/scripts/init_conference.py +489 -0
  82. package/assets/lithermes-plugin/skills/autoconference/scripts/style_presets.py +104 -0
  83. package/assets/lithermes-plugin/skills/autoconference/templates/code-performance.md +59 -0
  84. package/assets/lithermes-plugin/skills/autoconference/templates/debate-mode.md +50 -0
  85. package/assets/lithermes-plugin/skills/autoconference/templates/prompt-optimization.md +58 -0
  86. package/assets/lithermes-plugin/skills/autoconference/templates/quick-conference.md +48 -0
  87. package/assets/lithermes-plugin/skills/autoconference/templates/research-synthesis.md +56 -0
  88. package/assets/lithermes-plugin/skills/autoconference/templates/survey-mode.md +63 -0
  89. package/assets/lithermes-plugin/skills/autoresearch/LICENSE +21 -0
  90. package/assets/lithermes-plugin/skills/autoresearch/ORIGIN.json +11 -0
  91. package/assets/lithermes-plugin/skills/autoresearch/SKILL.md +175 -0
  92. package/assets/lithermes-plugin/skills/autoresearch/assets/report_template.md +50 -0
  93. package/assets/lithermes-plugin/skills/autoresearch/assets/research_template.md +38 -0
  94. package/assets/lithermes-plugin/skills/autoresearch/assets/results_template.tsv +2 -0
  95. package/assets/lithermes-plugin/skills/autoresearch/modes/core/SKILL.md +204 -0
  96. package/assets/lithermes-plugin/skills/autoresearch/modes/core/evaluator-contract.md +108 -0
  97. package/assets/lithermes-plugin/skills/autoresearch/modes/core/stuck-detection.md +102 -0
  98. package/assets/lithermes-plugin/skills/autoresearch/modes/debug/SKILL.md +240 -0
  99. package/assets/lithermes-plugin/skills/autoresearch/modes/debug/investigation-techniques.md +254 -0
  100. package/assets/lithermes-plugin/skills/autoresearch/modes/fix/SKILL.md +176 -0
  101. package/assets/lithermes-plugin/skills/autoresearch/modes/learn/SKILL.md +96 -0
  102. package/assets/lithermes-plugin/skills/autoresearch/modes/plan/SKILL.md +291 -0
  103. package/assets/lithermes-plugin/skills/autoresearch/modes/predict/SKILL.md +271 -0
  104. package/assets/lithermes-plugin/skills/autoresearch/modes/predict/persona-templates.md +227 -0
  105. package/assets/lithermes-plugin/skills/autoresearch/modes/reason/SKILL.md +174 -0
  106. package/assets/lithermes-plugin/skills/autoresearch/modes/scenario/SKILL.md +168 -0
  107. package/assets/lithermes-plugin/skills/autoresearch/modes/scenario/dimensions.md +173 -0
  108. package/assets/lithermes-plugin/skills/autoresearch/modes/security/SKILL.md +287 -0
  109. package/assets/lithermes-plugin/skills/autoresearch/modes/security/owasp-checklist.md +156 -0
  110. package/assets/lithermes-plugin/skills/autoresearch/modes/security/stride-model.md +191 -0
  111. package/assets/lithermes-plugin/skills/autoresearch/modes/ship/SKILL.md +99 -0
  112. package/assets/lithermes-plugin/skills/autoresearch/modes/ship/type-checklists.md +158 -0
  113. package/assets/lithermes-plugin/skills/autoresearch/references/core-principles.md +75 -0
  114. package/assets/lithermes-plugin/skills/autoresearch/references/results-logging.md +55 -0
  115. package/assets/lithermes-plugin/skills/autoresearch/references/visualization-guide.md +90 -0
  116. package/assets/lithermes-plugin/skills/autoresearch/scripts/check_progress.sh +139 -0
  117. package/assets/lithermes-plugin/skills/autoresearch/scripts/init_research.py +219 -0
  118. package/assets/lithermes-plugin/skills/autoresearch/scripts/style_presets.py +104 -0
  119. package/assets/lithermes-plugin/skills/browser-drive/SKILL.md +147 -0
  120. package/assets/lithermes-plugin/skills/browser-drive/scripts/capability_probe.py +528 -0
  121. package/assets/lithermes-plugin/skills/comment-checker/SKILL.md +162 -0
  122. package/assets/lithermes-plugin/skills/debugging/SKILL.md +271 -0
  123. package/assets/lithermes-plugin/skills/debugging/references/methodology/00-setup.md +108 -0
  124. package/assets/lithermes-plugin/skills/debugging/references/methodology/02-investigate.md +122 -0
  125. package/assets/lithermes-plugin/skills/debugging/references/methodology/04-oracle-triple.md +136 -0
  126. package/assets/lithermes-plugin/skills/debugging/references/methodology/05-escalate.md +69 -0
  127. package/assets/lithermes-plugin/skills/debugging/references/methodology/06-fix.md +116 -0
  128. package/assets/lithermes-plugin/skills/debugging/references/methodology/08-qa.md +94 -0
  129. package/assets/lithermes-plugin/skills/debugging/references/methodology/09-cleanup.md +164 -0
  130. package/assets/lithermes-plugin/skills/debugging/references/methodology/partial-runtime-evidence.md +229 -0
  131. package/assets/lithermes-plugin/skills/debugging/references/runtimes/bundled-js-binary.md +415 -0
  132. package/assets/lithermes-plugin/skills/debugging/references/runtimes/go.md +252 -0
  133. package/assets/lithermes-plugin/skills/debugging/references/runtimes/native-binary.md +484 -0
  134. package/assets/lithermes-plugin/skills/debugging/references/runtimes/node.md +260 -0
  135. package/assets/lithermes-plugin/skills/debugging/references/runtimes/python.md +248 -0
  136. package/assets/lithermes-plugin/skills/debugging/references/runtimes/rust.md +234 -0
  137. package/assets/lithermes-plugin/skills/debugging/references/tools/ghidra.md +212 -0
  138. package/assets/lithermes-plugin/skills/debugging/references/tools/playwright-cli.md +194 -0
  139. package/assets/lithermes-plugin/skills/debugging/references/tools/pwndbg.md +263 -0
  140. package/assets/lithermes-plugin/skills/debugging/references/tools/pwntools.md +265 -0
  141. package/assets/lithermes-plugin/skills/deep-interview/SKILL.md +419 -0
  142. package/assets/lithermes-plugin/skills/frontend-ui-ux/SKILL.md +49 -0
  143. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/.gitattributes +2 -0
  144. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/README.md +248 -0
  145. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/_INDEX.md +191 -0
  146. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/airbnb.md +393 -0
  147. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/airtable.md +92 -0
  148. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/apple.md +250 -0
  149. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/aside.md +209 -0
  150. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/binance.md +348 -0
  151. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/bmw.md +183 -0
  152. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/brutalist-skill.md +92 -0
  153. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/bugatti.md +271 -0
  154. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/cal.md +262 -0
  155. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/claude.md +315 -0
  156. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/clay.md +307 -0
  157. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/clickhouse.md +284 -0
  158. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/clone-from-url.md +65 -0
  159. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/cohere.md +269 -0
  160. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/coinbase.md +132 -0
  161. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/composio.md +310 -0
  162. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/cursor.md +312 -0
  163. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/design-system-architecture.md +244 -0
  164. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/elevenlabs.md +268 -0
  165. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/expo.md +284 -0
  166. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/ferrari.md +317 -0
  167. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/figma.md +223 -0
  168. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/framer.md +249 -0
  169. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/gpt-tasteskill.md +74 -0
  170. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/hashicorp.md +281 -0
  171. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/ibm.md +335 -0
  172. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/image-to-code-skill.md +1228 -0
  173. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/imagegen-brandkit.md +798 -0
  174. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/imagegen-frontend-mobile.md +1465 -0
  175. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/imagegen-frontend-web.md +987 -0
  176. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/intercom.md +149 -0
  177. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/kraken.md +128 -0
  178. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/lamborghini.md +291 -0
  179. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/layout-skill.md +107 -0
  180. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/lazyweb.md +77 -0
  181. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/linear.app.md +370 -0
  182. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/lovable.md +301 -0
  183. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/mastercard.md +368 -0
  184. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/meta.md +369 -0
  185. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/minimalist-skill.md +85 -0
  186. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/minimax.md +260 -0
  187. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/mintlify.md +329 -0
  188. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/miro.md +111 -0
  189. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/mistral.ai.md +264 -0
  190. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/mongodb.md +269 -0
  191. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/nike.md +366 -0
  192. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/notion.md +312 -0
  193. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/nvidia.md +296 -0
  194. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/ollama.md +270 -0
  195. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/opencode.ai.md +284 -0
  196. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/output-skill.md +49 -0
  197. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/pinterest.md +233 -0
  198. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/playstation.md +367 -0
  199. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/posthog.md +259 -0
  200. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/raycast.md +271 -0
  201. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/react-dev-tooling-skill.md +230 -0
  202. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/redesign-skill.md +178 -0
  203. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/renault.md +314 -0
  204. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/replicate.md +264 -0
  205. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/resend.md +306 -0
  206. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/revolut.md +188 -0
  207. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/runwayml.md +247 -0
  208. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/sanity.md +360 -0
  209. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/sentry.md +265 -0
  210. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/shopify.md +353 -0
  211. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/soft-skill.md +98 -0
  212. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/spacex.md +197 -0
  213. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/spotify.md +249 -0
  214. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/starbucks.md +583 -0
  215. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/stitch-design-example.md +121 -0
  216. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/stitch-skill.md +184 -0
  217. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/stripe.md +325 -0
  218. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/supabase.md +258 -0
  219. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/superhuman.md +255 -0
  220. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/taste-skill.md +1206 -0
  221. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/tesla.md +289 -0
  222. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/theverge.md +342 -0
  223. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/together.ai.md +266 -0
  224. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/uber.md +298 -0
  225. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/vercel.md +313 -0
  226. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/vodafone.md +426 -0
  227. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/voltagent.md +326 -0
  228. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/warp.md +256 -0
  229. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/webflow.md +95 -0
  230. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/wired.md +281 -0
  231. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/wise.md +176 -0
  232. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/x.ai.md +260 -0
  233. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/design/zapier.md +331 -0
  234. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/EVIDENCE.md +97 -0
  235. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/README.md +48 -0
  236. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/UPSTREAM.md +80 -0
  237. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/lane-a-direction.md +64 -0
  238. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/lane-b-execution.md +65 -0
  239. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/lane-c-review.md +65 -0
  240. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/lane-d-memory.md +83 -0
  241. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/orchestration.md +80 -0
  242. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/routing.md +79 -0
  243. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/LICENSE +21 -0
  244. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/agents/accessibility-reviewer.md +83 -0
  245. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/agents/content-writer.md +132 -0
  246. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/agents/design-builder.md +109 -0
  247. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/agents/design-critic.md +89 -0
  248. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/agents/design-lead.md +113 -0
  249. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/agents/design-scout.md +78 -0
  250. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/agents/design-strategist.md +121 -0
  251. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/agents/heuristic-evaluator.md +268 -0
  252. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/agents/inspiration-scout.md +107 -0
  253. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/agents/motion-designer.md +120 -0
  254. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/accessible-content/reference.md +101 -0
  255. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/adaptive-interfaces/reference.md +109 -0
  256. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/cognitive-accessibility/reference.md +107 -0
  257. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/design-debate/reference.md +199 -0
  258. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/design-debt-tracker/reference.md +174 -0
  259. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/design-handoff/reference.md +125 -0
  260. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/design-md/reference.md +106 -0
  261. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/design-retrospective/reference.md +266 -0
  262. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/design-review/reference.md +123 -0
  263. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/design-system-alignment/reference.md +120 -0
  264. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/designpowers-critique/reference.md +164 -0
  265. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/heuristic-evaluation/reference.md +85 -0
  266. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/inclusive-personas/reference.md +98 -0
  267. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/inspiration-scouting/reference.md +165 -0
  268. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/interaction-design/reference.md +122 -0
  269. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/motion-choreography/reference.md +81 -0
  270. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/research-planning/reference.md +96 -0
  271. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/responsive-patterns/reference.md +77 -0
  272. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/synthetic-user-testing/reference.md +192 -0
  273. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/taste-feedback/reference.md +165 -0
  274. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/taste-report/reference.md +78 -0
  275. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/token-architecture/reference.md +75 -0
  276. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/ui-composition/reference.md +117 -0
  277. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/usability-testing/reference.md +78 -0
  278. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/verification-before-shipping/reference.md +125 -0
  279. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/voice-and-tone/reference.md +79 -0
  280. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/designpowers/vendor/skills/writing-design-plans/reference.md +119 -0
  281. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/perfection/README.md +160 -0
  282. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/perfection/react-perf-tooling.md +127 -0
  283. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/README.md +659 -0
  284. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/data/charts.csv +26 -0
  285. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/data/colors.csv +162 -0
  286. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/data/icons.csv +106 -0
  287. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/data/landing.csv +35 -0
  288. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/data/products.csv +162 -0
  289. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/data/react-performance.csv +45 -0
  290. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/data/stacks/astro.csv +54 -0
  291. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/data/stacks/flutter.csv +53 -0
  292. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/data/stacks/html-tailwind.csv +56 -0
  293. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/data/stacks/jetpack-compose.csv +53 -0
  294. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/data/stacks/nextjs.csv +53 -0
  295. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/data/stacks/nuxt-ui.csv +51 -0
  296. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/data/stacks/nuxtjs.csv +59 -0
  297. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/data/stacks/react-native.csv +52 -0
  298. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/data/stacks/react.csv +54 -0
  299. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/data/stacks/shadcn.csv +61 -0
  300. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/data/stacks/svelte.csv +54 -0
  301. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/data/stacks/swiftui.csv +51 -0
  302. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/data/stacks/vue.csv +50 -0
  303. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/data/styles.csv +85 -0
  304. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/data/typography.csv +74 -0
  305. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/data/ui-reasoning.csv +162 -0
  306. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/data/ux-guidelines.csv +100 -0
  307. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/data/web-interface.csv +31 -0
  308. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/scripts/core.py +262 -0
  309. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/scripts/design_system.py +1148 -0
  310. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/corpus/ui-ux-db/scripts/search.py +114 -0
  311. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/legal/ATTRIBUTION.md +217 -0
  312. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/legal/LICENSE +21 -0
  313. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/legal/LICENSE-Apache-2.0.txt +201 -0
  314. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/_canonical-corpus/manifest.json +876 -0
  315. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/adaptive-layout.md +81 -0
  316. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/brand-and-imagery.md +84 -0
  317. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/complete-contract.md +457 -0
  318. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/composition.md +89 -0
  319. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/creative-directions.md +81 -0
  320. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/evidence-review.md +113 -0
  321. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/implementation-platforms.md +92 -0
  322. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/inclusive-interface.md +85 -0
  323. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/interaction-motion.md +87 -0
  324. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/operating-lanes.md +85 -0
  325. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/performance-delivery.md +85 -0
  326. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/product-direction.md +86 -0
  327. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/redesign-playbook.md +91 -0
  328. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/system-foundations.md +82 -0
  329. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/taste-direction.md +86 -0
  330. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/visual-language.md +85 -0
  331. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/visual-reconstruction.md +94 -0
  332. package/assets/lithermes-plugin/skills/frontend-ui-ux/resources/LICENSE +21 -0
  333. package/assets/lithermes-plugin/skills/frontend-ui-ux/resources/PROVENANCE.json +38 -0
  334. package/assets/lithermes-plugin/skills/frontend-ui-ux/resources/THIRD-PARTY-NOTICE.txt +14 -0
  335. package/assets/lithermes-plugin/skills/frontend-ui-ux/resources/design-intelligence.json +1 -0
  336. package/assets/lithermes-plugin/skills/frontend-ui-ux/resources/import-manifest.json +1060 -0
  337. package/assets/lithermes-plugin/skills/frontend-ui-ux/schemas/design-contract-v1alpha1.schema.json +264 -0
  338. package/assets/lithermes-plugin/skills/frontend-ui-ux/schemas/design-contract-v1beta1.schema.json +268 -0
  339. package/assets/lithermes-plugin/skills/frontend-ui-ux/schemas/design-contract-v1beta2.schema.json +280 -0
  340. package/assets/lithermes-plugin/skills/frontend-ui-ux/scripts/design_contract_validation.py +996 -0
  341. package/assets/lithermes-plugin/skills/frontend-ui-ux/scripts/design_intelligence.py +128 -0
  342. package/assets/lithermes-plugin/skills/frontend-ui-ux/scripts/design_runtime.py +98 -0
  343. package/assets/lithermes-plugin/skills/frontend-ui-ux/scripts/import_design_intelligence.py +56 -0
  344. package/assets/lithermes-plugin/skills/frontend-ui-ux/scripts/source_import.py +144 -0
  345. package/assets/lithermes-plugin/skills/frontend-ui-ux/scripts/source_manifest.py +65 -0
  346. package/assets/lithermes-plugin/skills/lit-burnoff/SKILL.md +448 -0
  347. package/assets/lithermes-plugin/skills/lit-burnoff-file/SKILL.md +254 -0
  348. package/assets/lithermes-plugin/skills/lit-code/SKILL.md +609 -0
  349. package/assets/lithermes-plugin/skills/lit-code/references/go/README.md +90 -0
  350. package/assets/lithermes-plugin/skills/lit-code/references/go/backend-stack.md +641 -0
  351. package/assets/lithermes-plugin/skills/lit-code/references/go/bootstrap.md +328 -0
  352. package/assets/lithermes-plugin/skills/lit-code/references/go/bubbletea-v2.md +359 -0
  353. package/assets/lithermes-plugin/skills/lit-code/references/go/cobra-stack.md +468 -0
  354. package/assets/lithermes-plugin/skills/lit-code/references/go/concurrency.md +362 -0
  355. package/assets/lithermes-plugin/skills/lit-code/references/go/data-modeling.md +329 -0
  356. package/assets/lithermes-plugin/skills/lit-code/references/go/error-handling.md +359 -0
  357. package/assets/lithermes-plugin/skills/lit-code/references/go/golangci-strict.md +236 -0
  358. package/assets/lithermes-plugin/skills/lit-code/references/go/grpc-connect.md +375 -0
  359. package/assets/lithermes-plugin/skills/lit-code/references/go/libraries.md +337 -0
  360. package/assets/lithermes-plugin/skills/lit-code/references/go/one-liners.md +202 -0
  361. package/assets/lithermes-plugin/skills/lit-code/references/go/sqlc-pgx.md +471 -0
  362. package/assets/lithermes-plugin/skills/lit-code/references/go/testing.md +467 -0
  363. package/assets/lithermes-plugin/skills/lit-code/references/go/type-patterns.md +298 -0
  364. package/assets/lithermes-plugin/skills/lit-code/references/python/README.md +314 -0
  365. package/assets/lithermes-plugin/skills/lit-code/references/python/async-anyio.md +442 -0
  366. package/assets/lithermes-plugin/skills/lit-code/references/python/data-modeling.md +233 -0
  367. package/assets/lithermes-plugin/skills/lit-code/references/python/data-processing.md +133 -0
  368. package/assets/lithermes-plugin/skills/lit-code/references/python/error-handling.md +218 -0
  369. package/assets/lithermes-plugin/skills/lit-code/references/python/fastapi-stack.md +316 -0
  370. package/assets/lithermes-plugin/skills/lit-code/references/python/httpx2-optimization.md +360 -0
  371. package/assets/lithermes-plugin/skills/lit-code/references/python/libraries.md +307 -0
  372. package/assets/lithermes-plugin/skills/lit-code/references/python/one-liners.md +268 -0
  373. package/assets/lithermes-plugin/skills/lit-code/references/python/orjson-stack.md +378 -0
  374. package/assets/lithermes-plugin/skills/lit-code/references/python/pydantic-ai.md +285 -0
  375. package/assets/lithermes-plugin/skills/lit-code/references/python/pyproject-strict.md +232 -0
  376. package/assets/lithermes-plugin/skills/lit-code/references/python/textual-tui.md +201 -0
  377. package/assets/lithermes-plugin/skills/lit-code/references/python/type-patterns.md +176 -0
  378. package/assets/lithermes-plugin/skills/lit-code/references/rust/README.md +317 -0
  379. package/assets/lithermes-plugin/skills/lit-code/references/rust/async-tokio.md +299 -0
  380. package/assets/lithermes-plugin/skills/lit-code/references/rust/axum-stack.md +467 -0
  381. package/assets/lithermes-plugin/skills/lit-code/references/rust/cargo-strict.md +317 -0
  382. package/assets/lithermes-plugin/skills/lit-code/references/rust/clap-stack.md +409 -0
  383. package/assets/lithermes-plugin/skills/lit-code/references/rust/concurrency.md +375 -0
  384. package/assets/lithermes-plugin/skills/lit-code/references/rust/libraries.md +439 -0
  385. package/assets/lithermes-plugin/skills/lit-code/references/rust/one-liners.md +291 -0
  386. package/assets/lithermes-plugin/skills/lit-code/references/rust/proptest-insta.md +429 -0
  387. package/assets/lithermes-plugin/skills/lit-code/references/rust/type-state.md +354 -0
  388. package/assets/lithermes-plugin/skills/lit-code/references/rust/unsafe-discipline.md +250 -0
  389. package/assets/lithermes-plugin/skills/lit-code/references/rust/zero-cost-safety.md +527 -0
  390. package/assets/lithermes-plugin/skills/lit-code/references/rust-ub/README.md +289 -0
  391. package/assets/lithermes-plugin/skills/lit-code/references/rust-ub/miri-sanitizers-loom.md +411 -0
  392. package/assets/lithermes-plugin/skills/lit-code/references/rust-ub/ub-taxonomy.md +269 -0
  393. package/assets/lithermes-plugin/skills/lit-code/references/typescript/README.md +195 -0
  394. package/assets/lithermes-plugin/skills/lit-code/references/typescript/backend-hono.md +672 -0
  395. package/assets/lithermes-plugin/skills/lit-code/references/typescript/bootstrap.md +199 -0
  396. package/assets/lithermes-plugin/skills/lit-code/references/typescript/data-modeling.md +202 -0
  397. package/assets/lithermes-plugin/skills/lit-code/references/typescript/error-handling.md +169 -0
  398. package/assets/lithermes-plugin/skills/lit-code/references/typescript/tsconfig-strict.md +152 -0
  399. package/assets/lithermes-plugin/skills/lit-code/references/typescript/type-patterns.md +196 -0
  400. package/assets/lithermes-plugin/skills/lit-code/scripts/go/check-no-excuse-rules.sh +173 -0
  401. package/assets/lithermes-plugin/skills/lit-code/scripts/go/new-project.py +138 -0
  402. package/assets/lithermes-plugin/skills/lit-code/scripts/go/templates/.editorconfig +13 -0
  403. package/assets/lithermes-plugin/skills/lit-code/scripts/go/templates/.golangci.yml +95 -0
  404. package/assets/lithermes-plugin/skills/lit-code/scripts/go/templates/AGENTS.md.tmpl +24 -0
  405. package/assets/lithermes-plugin/skills/lit-code/scripts/go/templates/README.md.tmpl +12 -0
  406. package/assets/lithermes-plugin/skills/lit-code/scripts/go/templates/Taskfile.yml +40 -0
  407. package/assets/lithermes-plugin/skills/lit-code/scripts/go/templates/ci.yml +37 -0
  408. package/assets/lithermes-plugin/skills/lit-code/scripts/go/templates/config.go +24 -0
  409. package/assets/lithermes-plugin/skills/lit-code/scripts/go/templates/gitignore +15 -0
  410. package/assets/lithermes-plugin/skills/lit-code/scripts/go/templates/main.go.tmpl +22 -0
  411. package/assets/lithermes-plugin/skills/lit-code/scripts/go/templates/run.go +15 -0
  412. package/assets/lithermes-plugin/skills/lit-code/scripts/python/check-no-excuse-rules.py +687 -0
  413. package/assets/lithermes-plugin/skills/lit-code/scripts/python/new-project.py +172 -0
  414. package/assets/lithermes-plugin/skills/lit-code/scripts/python/new-script.py +116 -0
  415. package/assets/lithermes-plugin/skills/lit-code/scripts/rust/check-no-excuse-rules.py +296 -0
  416. package/assets/lithermes-plugin/skills/lit-code/scripts/rust/check-no-excuse-rules.sh +158 -0
  417. package/assets/lithermes-plugin/skills/lit-code/scripts/rust/new-project.py +175 -0
  418. package/assets/lithermes-plugin/skills/lit-code/scripts/typescript/check-no-excuse-rules.ts +282 -0
  419. package/assets/lithermes-plugin/skills/lit-code/scripts/typescript/new-project.ts +177 -0
  420. package/assets/lithermes-plugin/skills/lit-commit/SKILL.md +242 -0
  421. package/assets/lithermes-plugin/skills/lit-comprehend/SKILL.md +259 -0
  422. package/assets/lithermes-plugin/skills/lit-comprehend/assets/explainer-scaffold.html +123 -0
  423. package/assets/lithermes-plugin/skills/lit-comprehend/references/artifact-template.md +205 -0
  424. package/assets/lithermes-plugin/skills/lit-comprehend/references/micro-worlds.md +139 -0
  425. package/assets/lithermes-plugin/skills/lit-comprehend/scripts/verify-explainer.py +324 -0
  426. package/assets/lithermes-plugin/skills/lit-crucible/SKILL.md +315 -0
  427. package/assets/lithermes-plugin/skills/lit-handoff/ORIGIN.json +23 -0
  428. package/assets/lithermes-plugin/skills/lit-handoff/SKILL.md +145 -0
  429. package/assets/lithermes-plugin/skills/lit-init/SKILL.md +329 -0
  430. package/assets/lithermes-plugin/skills/lit-korean/SKILL.md +237 -0
  431. package/assets/lithermes-plugin/skills/lit-korean/references/quick-rules.md +24 -0
  432. package/assets/lithermes-plugin/skills/lit-korean/references/safety-checklist.md +23 -0
  433. package/assets/lithermes-plugin/skills/lit-plan/SKILL.md +596 -0
  434. package/assets/lithermes-plugin/skills/lit-recap/SKILL.md +183 -0
  435. package/assets/lithermes-plugin/skills/lit-scientific-visualization/ORIGIN.json +32 -0
  436. package/assets/lithermes-plugin/skills/lit-scientific-visualization/SKILL.md +170 -0
  437. package/assets/lithermes-plugin/skills/litgoal/.gitkeep +0 -0
  438. package/assets/lithermes-plugin/skills/litgoal/SKILL.md +372 -0
  439. package/assets/lithermes-plugin/skills/litresearch/ATTRIBUTION.md +50 -0
  440. package/assets/lithermes-plugin/skills/litresearch/SKILL.md +593 -0
  441. package/assets/lithermes-plugin/skills/litwork/SKILL.md +419 -0
  442. package/assets/lithermes-plugin/skills/lsp/SKILL.md +165 -0
  443. package/assets/lithermes-plugin/skills/lsp-setup/SKILL.md +265 -0
  444. package/assets/lithermes-plugin/skills/lsp-setup/references/bash/README.md +68 -0
  445. package/assets/lithermes-plugin/skills/lsp-setup/references/c-cpp/README.md +78 -0
  446. package/assets/lithermes-plugin/skills/lsp-setup/references/csharp/README.md +90 -0
  447. package/assets/lithermes-plugin/skills/lsp-setup/references/dart/README.md +59 -0
  448. package/assets/lithermes-plugin/skills/lsp-setup/references/elixir/README.md +61 -0
  449. package/assets/lithermes-plugin/skills/lsp-setup/references/go/README.md +63 -0
  450. package/assets/lithermes-plugin/skills/lsp-setup/references/haskell/README.md +69 -0
  451. package/assets/lithermes-plugin/skills/lsp-setup/references/java/README.md +70 -0
  452. package/assets/lithermes-plugin/skills/lsp-setup/references/julia/README.md +70 -0
  453. package/assets/lithermes-plugin/skills/lsp-setup/references/kotlin/README.md +72 -0
  454. package/assets/lithermes-plugin/skills/lsp-setup/references/lua/README.md +68 -0
  455. package/assets/lithermes-plugin/skills/lsp-setup/references/php/README.md +61 -0
  456. package/assets/lithermes-plugin/skills/lsp-setup/references/python/README.md +73 -0
  457. package/assets/lithermes-plugin/skills/lsp-setup/references/ruby/README.md +67 -0
  458. package/assets/lithermes-plugin/skills/lsp-setup/references/rust/README.md +65 -0
  459. package/assets/lithermes-plugin/skills/lsp-setup/references/swift/README.md +64 -0
  460. package/assets/lithermes-plugin/skills/lsp-setup/references/terraform/README.md +66 -0
  461. package/assets/lithermes-plugin/skills/lsp-setup/references/typescript/README.md +82 -0
  462. package/assets/lithermes-plugin/skills/lsp-setup/references/yaml/README.md +65 -0
  463. package/assets/lithermes-plugin/skills/lsp-setup/references/zig/README.md +59 -0
  464. package/assets/lithermes-plugin/skills/lsp-setup/scripts/detect-lsp.ts +221 -0
  465. package/assets/lithermes-plugin/skills/lsp-setup/scripts/lsp-server-table.ts +146 -0
  466. package/assets/lithermes-plugin/skills/lsp-setup/scripts/tsconfig.json +18 -0
  467. package/assets/lithermes-plugin/skills/lsp-setup/scripts/verify-lsp.ts +252 -0
  468. package/assets/lithermes-plugin/skills/refactor/SKILL.md +896 -0
  469. package/assets/lithermes-plugin/skills/review-work/SKILL.md +450 -0
  470. package/assets/lithermes-plugin/skills/rules/SKILL.md +194 -0
  471. package/assets/lithermes-plugin/skills/skill-observer/SKILL.md +190 -0
  472. package/assets/lithermes-plugin/skills/skill-observer/references/review-contract.md +77 -0
  473. package/assets/lithermes-plugin/skills/start-work/SKILL.md +550 -0
  474. package/assets/lithermes-plugin/skills/structural-search/SKILL.md +274 -0
  475. package/assets/lithermes-plugin/skills/visual-qa/SKILL.md +48 -0
  476. package/assets/lithermes-plugin/skills/visual-qa/references/capture-playbook.md +133 -0
  477. package/assets/lithermes-plugin/skills/visual-qa/references/complete-contract.md +522 -0
  478. package/assets/lithermes-plugin/skills/visual-qa/schemas/evidence-manifest-v1alpha1.schema.json +135 -0
  479. package/assets/lithermes-plugin/skills/visual-qa/schemas/evidence-manifest-v1beta1.schema.json +110 -0
  480. package/assets/lithermes-plugin/skills/visual-qa/schemas/review-receipt-v1alpha1.schema.json +49 -0
  481. package/assets/lithermes-plugin/skills/visual-qa/scripts/contract_checks.py +71 -0
  482. package/assets/lithermes-plugin/skills/visual-qa/scripts/evidence_contract.py +288 -0
  483. package/assets/lithermes-plugin/skills/visual-qa/scripts/png_runtime.py +379 -0
  484. package/assets/lithermes-plugin/skills/visual-qa/scripts/review_contract.py +151 -0
  485. package/assets/lithermes-plugin/skills/visual-qa/scripts/tier_checks.py +190 -0
  486. package/assets/lithermes-plugin/skills/visual-qa/scripts/tier_contract.py +103 -0
  487. package/assets/lithermes-plugin/skills/visual-qa/scripts/tui_runtime.py +170 -0
  488. package/assets/lithermes-plugin/skills/visual-qa/scripts/visual_contracts.py +31 -0
  489. package/assets/lithermes-plugin/skills/visual-qa/scripts/visual_qa.py +79 -0
  490. package/assets/lithermes-plugin/skills/wikify/LICENSE +21 -0
  491. package/assets/lithermes-plugin/skills/wikify/ORIGIN.json +12 -0
  492. package/assets/lithermes-plugin/skills/wikify/SKILL.md +192 -0
  493. package/assets/lithermes-plugin/skills/wikify/assets/home-template.md +44 -0
  494. package/assets/lithermes-plugin/skills/wikify/assets/maintenance-report-template.md +46 -0
  495. package/assets/lithermes-plugin/skills/wikify/assets/paper-source-note-template.md +138 -0
  496. package/assets/lithermes-plugin/skills/wikify/assets/source-note-template.md +45 -0
  497. package/assets/lithermes-plugin/skills/wikify/assets/wiki-rules-template.md +117 -0
  498. package/assets/lithermes-plugin/skills/wikify/modes/ingest/SKILL.md +24 -0
  499. package/assets/lithermes-plugin/skills/wikify/modes/init/SKILL.md +26 -0
  500. package/assets/lithermes-plugin/skills/wikify/modes/lint/SKILL.md +30 -0
  501. package/assets/lithermes-plugin/skills/wikify/modes/query/SKILL.md +22 -0
  502. package/assets/lithermes-plugin/skills/wikify/modes/save/SKILL.md +32 -0
  503. package/assets/lithermes-plugin/skills/wikify/references/full-contract.md +761 -0
  504. package/assets/lithermes-plugin/uiux_runtime_common.py +117 -0
  505. package/assets/lithermes-plugin/vendor/NOTICE.md +21 -0
  506. package/assets/lithermes-plugin/vendor/handoff/SKILL.md +199 -0
  507. package/assets/lithermes-plugin/vendor/handoff/evals/evals.json +154 -0
  508. package/assets/lithermes-plugin/vendor/handoff/examples/HANDOFF-example-generic-auth-refactor.md +97 -0
  509. package/assets/lithermes-plugin/vendor/handoff/templates/HANDOFF.md +121 -0
  510. package/assets/lithermes-plugin/vendor/licenses/022_handoff-MIT.txt +21 -0
  511. package/assets/lithermes-plugin/vendor/licenses/045_scientific-visualization-MIT.txt +21 -0
  512. package/assets/lithermes-plugin/vendor/provenance/022_handoff.md +16 -0
  513. package/assets/lithermes-plugin/vendor/provenance/045_scientific-visualization.md +18 -0
  514. package/assets/lithermes-plugin/vendor/scientific-visualization/SKILL.md +283 -0
  515. package/assets/lithermes-plugin/vendor/scientific-visualization/assets/color_palettes.py +197 -0
  516. package/assets/lithermes-plugin/vendor/scientific-visualization/assets/nature.mplstyle +75 -0
  517. package/assets/lithermes-plugin/vendor/scientific-visualization/assets/presentation.mplstyle +74 -0
  518. package/assets/lithermes-plugin/vendor/scientific-visualization/assets/publication.mplstyle +78 -0
  519. package/assets/lithermes-plugin/vendor/scientific-visualization/evals/evals.json +158 -0
  520. package/assets/lithermes-plugin/vendor/scientific-visualization/references/color_palettes.md +380 -0
  521. package/assets/lithermes-plugin/vendor/scientific-visualization/references/journal_requirements.md +359 -0
  522. package/assets/lithermes-plugin/vendor/scientific-visualization/references/matplotlib_examples.md +608 -0
  523. package/assets/lithermes-plugin/vendor/scientific-visualization/references/mdanalysis_martini_visualization.md +85 -0
  524. package/assets/lithermes-plugin/vendor/scientific-visualization/references/publication_guidelines.md +217 -0
  525. package/assets/lithermes-plugin/vendor/scientific-visualization/references/seaborn_for_publications.md +293 -0
  526. package/assets/lithermes-plugin/vendor/scientific-visualization/scripts/figure_export.py +238 -0
  527. package/assets/lithermes-plugin/vendor/scientific-visualization/scripts/style_presets.py +467 -0
  528. package/assets/lithermes-plugin/vendor/scientific-visualization/tests/test_figure_export.py +51 -0
  529. package/assets/lithermes-plugin/vendor/scientific-visualization/tests/test_style_presets.py +114 -0
  530. package/bin/lithermes.js +8 -0
  531. package/package.json +58 -0
  532. package/readme-assets/badge-license.svg +1 -0
  533. package/readme-assets/badge-version.svg +1 -0
  534. package/readme-assets/cover.webp +0 -0
  535. package/readme-assets/lithermes-clay-icon.png +0 -0
  536. package/readme-assets/lithermes-wordmark.svg +5 -0
  537. package/src/cli.js +396 -0
  538. package/src/lib/autoUpdate.js +4 -0
  539. package/src/lib/canonicalCorpus.js +436 -0
  540. package/src/lib/check.js +200 -0
  541. package/src/lib/config.js +229 -0
  542. package/src/lib/files.js +126 -0
  543. package/src/lib/hermesDiscovery.js +122 -0
  544. package/src/lib/hud.js +135 -0
  545. package/src/lib/install.js +559 -0
  546. package/src/lib/litMark.js +68 -0
  547. package/src/lib/modelConfig.js +431 -0
  548. package/src/lib/modelConfigBoundary.js +206 -0
  549. package/src/lib/modelPrompt.js +201 -0
  550. package/src/lib/modelRoutePolicy.js +379 -0
  551. package/src/lib/patch.js +153 -0
  552. package/src/lib/skillLoop.js +62 -0
  553. package/src/lib/skillPayload.js +306 -0
  554. package/src/lib/skins.js +274 -0
  555. package/src/lib/spinner.js +193 -0
  556. package/src/lib/updateNotifier.js +1133 -0
  557. package/src/lib/updateNotifierWorker.js +9 -0
@@ -0,0 +1,996 @@
1
+ """Executable rules for litfamily.design-contract/v1alpha1.
2
+
3
+ `schemas/design-contract-v1alpha1.schema.json` states the document shape and is
4
+ useful for editor tooling, but a JSON Schema pass alone accepts contracts that are
5
+ already unusable. It cannot resolve a `route_id` pointer, cannot notice that one id
6
+ was reused in two inventory groups, cannot couple `auth_required` routes to their
7
+ authenticated surfaces, cannot see a duplicated JSON key that the parser already
8
+ collapsed, and cannot decide whether a timestamp round-trips. Those rules live in
9
+ this module, and this module wins whenever the two disagree.
10
+
11
+ Every check appends to an issue list instead of raising, so one pass reports the
12
+ whole set of violations rather than the first one.
13
+ """
14
+ from __future__ import annotations
15
+
16
+ import json
17
+ import re
18
+ import stat
19
+ import sys
20
+ from collections import Counter
21
+ from datetime import datetime
22
+ from pathlib import Path
23
+ from typing import Final, Optional
24
+
25
+ from uiux_runtime_common import ContractError, JsonValue, parse_json_bytes
26
+
27
+ SCHEMA_ID: Final = "litfamily.design-contract/v1alpha1"
28
+ BETA_SCHEMA_ID: Final = "litfamily.design-contract/v1beta1"
29
+ BETA2_SCHEMA_ID: Final = "litfamily.design-contract/v1beta2"
30
+ UNTRUSTED: Final = "CONTRACT_INPUT_UNTRUSTED"
31
+ MAX_CONTRACT_BYTES: Final = 1024 * 1024
32
+ MAX_NESTING_DEPTH: Final = 64
33
+ MAX_BETA_TEXT_CHARACTERS: Final = 512
34
+ ACCESSIBILITY_TARGET: Final = "WCAG 2.2 AA"
35
+ UTC_INSTANT: Final = "%Y-%m-%dT%H:%M:%SZ"
36
+
37
+ # `fullmatch` rather than the `^...$` form, because Python's `$` also matches just
38
+ # before a trailing newline and would let "<64 hex>\n" through as a hash.
39
+ ID_GRAMMAR: Final = re.compile(r"[a-z][a-z0-9-]*:[a-z0-9][a-z0-9._/-]*")
40
+ HASH_GRAMMAR: Final = re.compile(r"[0-9a-f]{64}")
41
+
42
+ TOP_KEYS: Final = (
43
+ "schema_id", "contract_id", "source_hash", "intent", "direction", "inventory",
44
+ "accessibility", "localization", "performance", "evidence_policy", "omissions",
45
+ "accepted_exceptions",
46
+ )
47
+ BETA_TOP_FIELDS: Final = TOP_KEYS + (
48
+ "lane", "tokens", "component_behaviors", "responsive_transformations", "motion",
49
+ "acceptance_criteria",
50
+ )
51
+ # v1beta2 adds one optional root key. Optional is the whole point: a contract that
52
+ # says nothing about taste is complete, and a v1beta1 document keeps its meaning.
53
+ BETA2_OPTIONAL_FIELDS: Final = ("taste",)
54
+ # Direction expressed as numbers a reviewer can argue with, rather than adjectives
55
+ # they cannot.
56
+ TASTE_DIALS: Final = ("variance", "motion", "density")
57
+ TASTE_LOW: Final = 1
58
+ TASTE_HIGH: Final = 10
59
+
60
+ INTENT_KEYS: Final = ("audiences", "tasks", "qualities", "constraints", "non_goals")
61
+ DIRECTION_KEYS: Final = ("name", "principles", "token_strategy", "voice")
62
+ INVENTORY_KEYS: Final = (
63
+ "routes", "regions", "components", "interactions", "states", "viewports",
64
+ "references", "authenticated_surfaces",
65
+ )
66
+ ACCESSIBILITY_KEYS: Final = (
67
+ "target", "keyboard", "screen_reader", "reduced_motion", "forced_colors", "zoom_percent",
68
+ )
69
+ LOCALIZATION_KEYS: Final = (
70
+ "locales", "text_expansion_percent", "cjk_line_break_review", "font_fallback_review",
71
+ "ime_review", "rtl_review",
72
+ )
73
+ PERFORMANCE_KEYS: Final = ("lcp_ms", "cls", "inp_ms", "initial_js_kb", "initial_css_kb")
74
+ EVIDENCE_POLICY_KEYS: Final = (
75
+ "independent_review_required", "required_channels", "cleanup_required",
76
+ )
77
+ DISCLOSURE_KEYS: Final = ("id", "reason", "owner")
78
+ ROUTE_KEYS: Final = ("id", "path", "priority", "auth_required")
79
+ REGION_KEYS: Final = ("id", "route_id", "purpose")
80
+ COMPONENT_KEYS: Final = ("id", "region_id", "role")
81
+ INTERACTION_KEYS: Final = ("id", "route_id", "trigger", "outcome", "criticality", "input_modes")
82
+ STATE_KEYS: Final = ("id", "route_id", "kind")
83
+ VIEWPORT_KEYS: Final = ("id", "category", "width_px", "height_px")
84
+ REFERENCE_KEYS: Final = ("id", "kind", "sha256")
85
+ SURFACE_KEYS: Final = ("route_id", "safe_test_account")
86
+
87
+ TOKEN_STRATEGIES: Final = ("reuse", "extend", "create")
88
+ ROUTE_PRIORITIES: Final = ("primary", "secondary")
89
+ INTERACTION_CRITICALITY: Final = ("critical", "supporting")
90
+ INPUT_MODES: Final = ("keyboard", "pointer", "touch", "voice", "switch")
91
+ STATE_KINDS: Final = (
92
+ "loading", "empty", "error", "success", "disabled", "permission", "offline", "ready",
93
+ )
94
+ VIEWPORT_CATEGORIES: Final = ("compact", "medium", "expanded")
95
+ REFERENCE_KINDS: Final = ("user-provided", "repo-local", "generated", "measured")
96
+ EVIDENCE_CHANNELS: Final = (
97
+ "tests", "browser", "keyboard", "accessibility-tree", "screen-reader", "performance",
98
+ "localization",
99
+ )
100
+ LANES: Final = ("new-build", "brownfield", "redesign", "reference-fidelity", "design-system")
101
+ TOKEN_CATEGORIES: Final = ("color", "typography", "spacing", "radius", "shadow", "motion", "other")
102
+ MOTION_POLICIES: Final = ("none", "functional", "expressive")
103
+ VERIFICATION_METHODS: Final = EVIDENCE_CHANNELS + ("manual",)
104
+
105
+ _WHITESPACE: Final = " \t\n\r"
106
+ _STRING_ESCAPES: Final = {
107
+ '"': '"', "\\": "\\", "/": "/", "b": "\b", "f": "\f", "n": "\n", "r": "\r", "t": "\t",
108
+ }
109
+ _HEX_DIGITS: Final = "0123456789abcdefABCDEF"
110
+ _SCALAR_STOP: Final = ",}]" + _WHITESPACE
111
+
112
+
113
+ # ---------------------------------------------------------------------------
114
+ # Bounded, pre-parse input screening
115
+ # ---------------------------------------------------------------------------
116
+
117
+
118
+ def _untrusted(detail: str) -> None:
119
+ raise ContractError(UNTRUSTED, detail)
120
+
121
+
122
+ def _skip_whitespace(text: str, index: int) -> int:
123
+ while index < len(text) and text[index] in _WHITESPACE:
124
+ index += 1
125
+ return index
126
+
127
+
128
+ def _scan_string(text: str, index: int) -> tuple:
129
+ """Read one JSON string literal starting at the opening quote.
130
+
131
+ Returns the decoded text and the index just past the closing quote. Raw control
132
+ characters are rejected here so a smuggled newline or escape byte inside a key or
133
+ value never reaches the parser.
134
+ """
135
+ index += 1
136
+ pieces = []
137
+ while True:
138
+ if index >= len(text):
139
+ _untrusted("unterminated string literal")
140
+ character = text[index]
141
+ if character == '"':
142
+ return "".join(pieces), index + 1
143
+ if character == "\\":
144
+ if index + 1 >= len(text):
145
+ _untrusted("truncated escape sequence")
146
+ escape = text[index + 1]
147
+ if escape == "u":
148
+ digits = text[index + 2:index + 6]
149
+ if len(digits) != 4 or any(digit not in _HEX_DIGITS for digit in digits):
150
+ _untrusted("malformed \\u escape sequence")
151
+ pieces.append(chr(int(digits, 16)))
152
+ index += 6
153
+ continue
154
+ if escape not in _STRING_ESCAPES:
155
+ _untrusted("unknown escape sequence: \\" + escape)
156
+ pieces.append(_STRING_ESCAPES[escape])
157
+ index += 2
158
+ continue
159
+ if ord(character) < 0x20:
160
+ _untrusted("raw control character inside a string literal")
161
+ pieces.append(character)
162
+ index += 1
163
+
164
+
165
+ def _scan_object(text: str, index: int, depth: int) -> int:
166
+ # A fresh key set per object per depth: repeating a key inside one object fails,
167
+ # while the same key name at another depth or in a sibling object stays legal.
168
+ keys = set()
169
+ index = _skip_whitespace(text, index + 1)
170
+ if index < len(text) and text[index] == "}":
171
+ return index + 1
172
+ while True:
173
+ index = _skip_whitespace(text, index)
174
+ if index >= len(text) or text[index] != '"':
175
+ _untrusted("object member name must be a string literal")
176
+ key, index = _scan_string(text, index)
177
+ if key in keys:
178
+ _untrusted("duplicate object key: " + key)
179
+ keys.add(key)
180
+ index = _skip_whitespace(text, index)
181
+ if index >= len(text) or text[index] != ":":
182
+ _untrusted("object member is missing its name separator")
183
+ index = _scan_value(text, index + 1, depth + 1)
184
+ index = _skip_whitespace(text, index)
185
+ if index >= len(text):
186
+ _untrusted("unterminated object")
187
+ if text[index] == ",":
188
+ index += 1
189
+ continue
190
+ if text[index] == "}":
191
+ return index + 1
192
+ _untrusted("malformed object member separator")
193
+
194
+
195
+ def _scan_array(text: str, index: int, depth: int) -> int:
196
+ index = _skip_whitespace(text, index + 1)
197
+ if index < len(text) and text[index] == "]":
198
+ return index + 1
199
+ while True:
200
+ index = _scan_value(text, index, depth + 1)
201
+ index = _skip_whitespace(text, index)
202
+ if index >= len(text):
203
+ _untrusted("unterminated array")
204
+ if text[index] == ",":
205
+ index += 1
206
+ continue
207
+ if text[index] == "]":
208
+ return index + 1
209
+ _untrusted("malformed array element separator")
210
+
211
+
212
+ def _scan_value(text: str, index: int, depth: int) -> int:
213
+ if depth > MAX_NESTING_DEPTH:
214
+ _untrusted("nesting deeper than {0} levels".format(MAX_NESTING_DEPTH))
215
+ index = _skip_whitespace(text, index)
216
+ if index >= len(text):
217
+ _untrusted("truncated JSON value")
218
+ character = text[index]
219
+ if character == "{":
220
+ return _scan_object(text, index, depth)
221
+ if character == "[":
222
+ return _scan_array(text, index, depth)
223
+ if character == '"':
224
+ return _scan_string(text, index)[1]
225
+ # Numbers and the bare literals cannot contain a structural character, so walking
226
+ # to the next one is enough; json.loads decides whether the token is well formed.
227
+ start = index
228
+ while index < len(text) and text[index] not in _SCALAR_STOP:
229
+ index += 1
230
+ if index == start:
231
+ _untrusted("unexpected character: " + character)
232
+ return index
233
+
234
+
235
+ def scan_contract_text(text: str) -> None:
236
+ """Walk the raw document before it is parsed.
237
+
238
+ The load-bearing reason is duplicate keys: json.loads keeps the last value for a
239
+ repeated key and reports nothing, so a contract can silently disagree with the text
240
+ that was reviewed and hashed. Control-character, framing, and depth checks fall out
241
+ of the same walk, which means an untrustworthy payload is rejected before any value
242
+ is materialised.
243
+ """
244
+ if "\x00" in text:
245
+ _untrusted("payload contains a NUL byte")
246
+ end = _scan_value(text, 0, 0)
247
+ if text[_skip_whitespace(text, end):]:
248
+ _untrusted("trailing data after the JSON document")
249
+
250
+
251
+ def load_design_contract_bytes(raw: bytes) -> JsonValue:
252
+ """Decode and screen a contract payload, or refuse it outright.
253
+
254
+ Raises ContractError with code CONTRACT_INPUT_UNTRUSTED when the bytes cannot be
255
+ trusted enough to check against the rules at all.
256
+ """
257
+ if len(raw) > MAX_CONTRACT_BYTES:
258
+ _untrusted("{0} bytes exceeds the {1}-byte cap".format(len(raw), MAX_CONTRACT_BYTES))
259
+ try:
260
+ text = raw.decode("utf-8", errors="strict")
261
+ except UnicodeDecodeError as error:
262
+ raise ContractError(UNTRUSTED, "payload is not valid UTF-8: {0}".format(error)) from error
263
+ scan_contract_text(text)
264
+ try:
265
+ return parse_json_bytes(raw, maximum_bytes=MAX_CONTRACT_BYTES)
266
+ except ContractError as error:
267
+ raise ContractError(UNTRUSTED, "{0}: {1}".format(error.code, error.detail)) from error
268
+
269
+
270
+ def load_design_contract_path(path: Path) -> JsonValue:
271
+ """Read a contract from a path, refusing anything that is not a bounded regular file."""
272
+ try:
273
+ status = path.lstat()
274
+ except OSError as error:
275
+ raise ContractError(UNTRUSTED, "contract path is unreadable: {0}".format(error)) from error
276
+ if path.is_symlink() or not stat.S_ISREG(status.st_mode):
277
+ _untrusted("contract path is not a regular file: {0}".format(path))
278
+ if status.st_size > MAX_CONTRACT_BYTES:
279
+ _untrusted("{0} bytes exceeds the {1}-byte cap".format(status.st_size, MAX_CONTRACT_BYTES))
280
+ try:
281
+ raw = path.read_bytes()
282
+ except OSError as error:
283
+ raise ContractError(UNTRUSTED, "contract path is unreadable: {0}".format(error)) from error
284
+ return load_design_contract_bytes(raw)
285
+
286
+
287
+ def load_design_contract_stdin() -> JsonValue:
288
+ return load_design_contract_bytes(sys.stdin.buffer.read(MAX_CONTRACT_BYTES + 1))
289
+
290
+
291
+ def canonical_design_contract(value: JsonValue) -> str:
292
+ """Canonical text for hashing.
293
+
294
+ Object keys are sorted at every depth, array order is preserved, and the text ends
295
+ with exactly one newline. That newline is part of the canonical form: every design
296
+ contract digest in this plugin is taken over this exact text, so dropping it changes
297
+ every hash the evidence trail pins.
298
+ """
299
+ return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + "\n"
300
+
301
+
302
+ # ---------------------------------------------------------------------------
303
+ # Primitive rules
304
+ # ---------------------------------------------------------------------------
305
+
306
+
307
+ def _mapping(issues: list, value: JsonValue, keys: tuple, label: str, optional: tuple = ()):
308
+ if not isinstance(value, dict):
309
+ issues.append("{0} must be an object".format(label))
310
+ return None
311
+ missing = sorted(set(keys) - set(value))
312
+ unknown = sorted(set(value) - set(keys) - set(optional))
313
+ if missing:
314
+ issues.append("{0} is missing {1}".format(label, missing))
315
+ if unknown:
316
+ issues.append("{0} has unapproved keys {1}".format(label, unknown))
317
+ return value
318
+
319
+
320
+ def _text(issues: list, value: JsonValue, label: str, maximum: Optional[int] = None):
321
+ if not isinstance(value, str) or not value.strip():
322
+ issues.append("{0} must be a non-empty string".format(label))
323
+ return None
324
+ if maximum is not None and len(value) > maximum:
325
+ issues.append("{0} must contain at most {1} characters".format(label, maximum))
326
+ return None
327
+ return value
328
+
329
+
330
+ def _enum(issues: list, value: JsonValue, allowed: tuple, label: str):
331
+ if not isinstance(value, str) or value not in allowed:
332
+ issues.append("{0} must be one of {1}".format(label, list(allowed)))
333
+ return None
334
+ return value
335
+
336
+
337
+ def _boolean(issues: list, value: JsonValue, label: str) -> None:
338
+ if not isinstance(value, bool):
339
+ issues.append("{0} must be a JSON boolean".format(label))
340
+
341
+
342
+ def _integer(issues: list, value: JsonValue, label: str, low: int, high: int) -> None:
343
+ if isinstance(value, bool) or not isinstance(value, int) or not low <= value <= high:
344
+ issues.append("{0} must be an integer in {1}..{2}".format(label, low, high))
345
+
346
+
347
+ def _number(issues: list, value: JsonValue, label: str, low: float, high: float) -> None:
348
+ if isinstance(value, bool) or not isinstance(value, (int, float)) or not low <= value <= high:
349
+ issues.append("{0} must be a number in {1}..{2}".format(label, low, high))
350
+
351
+
352
+ def _hash(issues: list, value: JsonValue, label: str) -> None:
353
+ text = _text(issues, value, label)
354
+ if text is not None and HASH_GRAMMAR.fullmatch(text) is None:
355
+ issues.append("{0} must be a lowercase 64-character sha256".format(label))
356
+
357
+
358
+ def _identifier(issues: list, value: JsonValue, label: str):
359
+ text = _text(issues, value, label)
360
+ if text is None:
361
+ return None
362
+ if ID_GRAMMAR.fullmatch(text) is None:
363
+ issues.append("{0} must match the id grammar <kind>:<slug>, lowercase only".format(label))
364
+ return None
365
+ return text
366
+
367
+
368
+ def _typed_identifier(issues: list, value: JsonValue, prefix: str, label: str):
369
+ text = _identifier(issues, value, label)
370
+ if text is None:
371
+ return None
372
+ if text.split(":", 1)[0] != prefix:
373
+ issues.append("{0} must carry the '{1}:' type prefix".format(label, prefix))
374
+ return None
375
+ return text
376
+
377
+
378
+ def _resolve(issues: list, value: JsonValue, prefix: str, declared, label: str) -> None:
379
+ """Check a cross-reference: right type prefix, and the target is actually declared."""
380
+ identity = _typed_identifier(issues, value, prefix, label)
381
+ if identity is not None and identity not in declared:
382
+ issues.append(
383
+ "{0} does not resolve to a declared {1}: {2}".format(label, prefix, identity)
384
+ )
385
+
386
+
387
+ def _utc_instant(issues: list, value: JsonValue, label: str) -> None:
388
+ text = _text(issues, value, label)
389
+ if text is None:
390
+ return
391
+ try:
392
+ parsed = datetime.strptime(text, UTC_INSTANT)
393
+ except ValueError:
394
+ issues.append("{0} must be an ISO-8601 UTC instant like 2026-08-24T00:00:00Z".format(label))
395
+ return
396
+ # strptime matches literal characters case-insensitively and accepts single-digit
397
+ # fields, so only the round trip proves the text was already canonical. This is what
398
+ # rejects a lowercase z, a "+00:00" offset that slipped past, and 2026-8-4.
399
+ if parsed.strftime(UTC_INSTANT) != text:
400
+ issues.append(
401
+ "{0} must round-trip exactly as 2026-08-24T00:00:00Z: no offset, no fractional"
402
+ " seconds, uppercase T and Z".format(label)
403
+ )
404
+
405
+
406
+ def _strings(issues: list, value: JsonValue, label: str, minimum: int = 1, maximum=None) -> list:
407
+ if not isinstance(value, list):
408
+ issues.append("{0} must be an array".format(label))
409
+ return []
410
+ items = []
411
+ for position, candidate in enumerate(value):
412
+ text = _text(issues, candidate, "{0}[{1}]".format(label, position))
413
+ if text is not None:
414
+ items.append(text)
415
+ if len(value) < minimum:
416
+ issues.append("{0} must list at least {1} entries".format(label, minimum))
417
+ if maximum is not None and len(value) > maximum:
418
+ issues.append("{0} must list at most {1} entries".format(label, maximum))
419
+ if len(set(items)) != len(items):
420
+ issues.append("{0} entries must be unique".format(label))
421
+ return items
422
+
423
+
424
+ def _objects(issues: list, value: JsonValue, label: str, minimum: int = 0) -> list:
425
+ if not isinstance(value, list):
426
+ issues.append("{0} must be an array".format(label))
427
+ return []
428
+ if len(value) < minimum:
429
+ issues.append("{0} must list at least {1} entries".format(label, minimum))
430
+ return list(enumerate(value))
431
+
432
+
433
+ # ---------------------------------------------------------------------------
434
+ # Group rules
435
+ # ---------------------------------------------------------------------------
436
+
437
+
438
+ def _intent(issues: list, value: JsonValue) -> None:
439
+ intent = _mapping(issues, value, INTENT_KEYS, "intent")
440
+ if intent is None:
441
+ return
442
+ for key in ("audiences", "tasks", "qualities"):
443
+ _strings(issues, intent.get(key), "intent.{0}".format(key))
444
+ for key in ("constraints", "non_goals"):
445
+ _strings(issues, intent.get(key), "intent.{0}".format(key), minimum=0)
446
+
447
+
448
+ def _direction(issues: list, value: JsonValue) -> None:
449
+ direction = _mapping(issues, value, DIRECTION_KEYS, "direction")
450
+ if direction is None:
451
+ return
452
+ _text(issues, direction.get("name"), "direction.name")
453
+ _strings(issues, direction.get("principles"), "direction.principles", minimum=3, maximum=7)
454
+ _enum(issues, direction.get("token_strategy"), TOKEN_STRATEGIES, "direction.token_strategy")
455
+ _text(issues, direction.get("voice"), "direction.voice")
456
+
457
+
458
+ def _accessibility(issues: list, value: JsonValue) -> None:
459
+ accessibility = _mapping(issues, value, ACCESSIBILITY_KEYS, "accessibility")
460
+ if accessibility is None:
461
+ return
462
+ # Strict equality, not a pattern and not an enum: this plugin ships one target.
463
+ if accessibility.get("target") != ACCESSIBILITY_TARGET:
464
+ issues.append("accessibility.target must equal '{0}'".format(ACCESSIBILITY_TARGET))
465
+ for key in ("keyboard", "screen_reader", "reduced_motion", "forced_colors"):
466
+ _boolean(issues, accessibility.get(key), "accessibility.{0}".format(key))
467
+ _integer(issues, accessibility.get("zoom_percent"), "accessibility.zoom_percent", 200, 400)
468
+
469
+
470
+ def _localization(issues: list, value: JsonValue) -> None:
471
+ localization = _mapping(issues, value, LOCALIZATION_KEYS, "localization")
472
+ if localization is None:
473
+ return
474
+ _strings(issues, localization.get("locales"), "localization.locales")
475
+ _integer(
476
+ issues,
477
+ localization.get("text_expansion_percent"),
478
+ "localization.text_expansion_percent",
479
+ 0,
480
+ 300,
481
+ )
482
+ for key in ("cjk_line_break_review", "font_fallback_review", "ime_review", "rtl_review"):
483
+ _boolean(issues, localization.get(key), "localization.{0}".format(key))
484
+
485
+
486
+ def _performance(issues: list, value: JsonValue) -> None:
487
+ performance = _mapping(issues, value, PERFORMANCE_KEYS, "performance")
488
+ if performance is None:
489
+ return
490
+ # Every dimension is bounded on both sides; an open-ended budget is not a budget.
491
+ _integer(issues, performance.get("lcp_ms"), "performance.lcp_ms", 1, 60000)
492
+ _number(issues, performance.get("cls"), "performance.cls", 0, 1)
493
+ _integer(issues, performance.get("inp_ms"), "performance.inp_ms", 1, 60000)
494
+ _integer(issues, performance.get("initial_js_kb"), "performance.initial_js_kb", 0, 1048576)
495
+ _integer(issues, performance.get("initial_css_kb"), "performance.initial_css_kb", 0, 1048576)
496
+
497
+
498
+ def _evidence_policy(issues: list, value: JsonValue) -> None:
499
+ policy = _mapping(issues, value, EVIDENCE_POLICY_KEYS, "evidence_policy")
500
+ if policy is None:
501
+ return
502
+ _boolean(
503
+ issues,
504
+ policy.get("independent_review_required"),
505
+ "evidence_policy.independent_review_required",
506
+ )
507
+ channels = _strings(
508
+ issues, policy.get("required_channels"), "evidence_policy.required_channels"
509
+ )
510
+ unknown = sorted(set(channels) - set(EVIDENCE_CHANNELS))
511
+ if unknown:
512
+ issues.append(
513
+ "evidence_policy.required_channels has unapproved channels {0}; allowed: {1}".format(
514
+ unknown, list(EVIDENCE_CHANNELS)
515
+ )
516
+ )
517
+ _boolean(issues, policy.get("cleanup_required"), "evidence_policy.cleanup_required")
518
+
519
+
520
+ def _disclosures(issues: list, value: JsonValue, field: str) -> list:
521
+ collected = []
522
+ for position, candidate in _objects(issues, value, field):
523
+ label = "{0}[{1}]".format(field, position)
524
+ entry = _mapping(issues, candidate, DISCLOSURE_KEYS, label, optional=("expires_at",))
525
+ if entry is None:
526
+ continue
527
+ identity = _identifier(issues, entry.get("id"), "{0}.id".format(label))
528
+ if identity is not None:
529
+ collected.append(identity)
530
+ _text(issues, entry.get("reason"), "{0}.reason".format(label))
531
+ _text(issues, entry.get("owner"), "{0}.owner".format(label))
532
+ if "expires_at" in entry:
533
+ _utc_instant(issues, entry.get("expires_at"), "{0}.expires_at".format(label))
534
+ return collected
535
+
536
+
537
+ def _routes(issues: list, value: JsonValue, ids: list) -> dict:
538
+ routes = {}
539
+ priorities = []
540
+ for position, candidate in _objects(issues, value, "inventory.routes", minimum=1):
541
+ label = "inventory.routes[{0}]".format(position)
542
+ route = _mapping(issues, candidate, ROUTE_KEYS, label)
543
+ if route is None:
544
+ continue
545
+ identity = _typed_identifier(issues, route.get("id"), "route", "{0}.id".format(label))
546
+ _text(issues, route.get("path"), "{0}.path".format(label))
547
+ priority = _enum(
548
+ issues, route.get("priority"), ROUTE_PRIORITIES, "{0}.priority".format(label)
549
+ )
550
+ _boolean(issues, route.get("auth_required"), "{0}.auth_required".format(label))
551
+ if priority is not None:
552
+ priorities.append(priority)
553
+ if identity is not None:
554
+ ids.append(identity)
555
+ routes[identity] = route.get("auth_required") is True
556
+ if "primary" not in priorities:
557
+ issues.append("inventory.routes must mark at least one route as 'primary'")
558
+ return routes
559
+
560
+
561
+ def _regions(issues: list, value: JsonValue, routes: dict, ids: list) -> set:
562
+ regions = set()
563
+ for position, candidate in _objects(issues, value, "inventory.regions", minimum=1):
564
+ label = "inventory.regions[{0}]".format(position)
565
+ region = _mapping(issues, candidate, REGION_KEYS, label)
566
+ if region is None:
567
+ continue
568
+ identity = _typed_identifier(issues, region.get("id"), "region", "{0}.id".format(label))
569
+ _resolve(issues, region.get("route_id"), "route", routes, "{0}.route_id".format(label))
570
+ _text(issues, region.get("purpose"), "{0}.purpose".format(label))
571
+ if identity is not None:
572
+ ids.append(identity)
573
+ regions.add(identity)
574
+ return regions
575
+
576
+
577
+ def _components(issues: list, value: JsonValue, regions: set, ids: list) -> None:
578
+ for position, candidate in _objects(issues, value, "inventory.components", minimum=1):
579
+ label = "inventory.components[{0}]".format(position)
580
+ component = _mapping(issues, candidate, COMPONENT_KEYS, label)
581
+ if component is None:
582
+ continue
583
+ identity = _typed_identifier(
584
+ issues, component.get("id"), "component", "{0}.id".format(label)
585
+ )
586
+ _resolve(
587
+ issues, component.get("region_id"), "region", regions, "{0}.region_id".format(label)
588
+ )
589
+ _text(issues, component.get("role"), "{0}.role".format(label))
590
+ if identity is not None:
591
+ ids.append(identity)
592
+
593
+
594
+ def _interactions(issues: list, value: JsonValue, routes: dict, ids: list) -> None:
595
+ criticality = []
596
+ for position, candidate in _objects(issues, value, "inventory.interactions", minimum=1):
597
+ label = "inventory.interactions[{0}]".format(position)
598
+ interaction = _mapping(issues, candidate, INTERACTION_KEYS, label)
599
+ if interaction is None:
600
+ continue
601
+ identity = _typed_identifier(
602
+ issues, interaction.get("id"), "interaction", "{0}.id".format(label)
603
+ )
604
+ _resolve(
605
+ issues, interaction.get("route_id"), "route", routes, "{0}.route_id".format(label)
606
+ )
607
+ _text(issues, interaction.get("trigger"), "{0}.trigger".format(label))
608
+ _text(issues, interaction.get("outcome"), "{0}.outcome".format(label))
609
+ weight = _enum(
610
+ issues,
611
+ interaction.get("criticality"),
612
+ INTERACTION_CRITICALITY,
613
+ "{0}.criticality".format(label),
614
+ )
615
+ modes = _strings(
616
+ issues, interaction.get("input_modes"), "{0}.input_modes".format(label)
617
+ )
618
+ unknown = sorted(set(modes) - set(INPUT_MODES))
619
+ if unknown:
620
+ issues.append(
621
+ "{0}.input_modes has unapproved modes {1}; allowed: {2}".format(
622
+ label, unknown, list(INPUT_MODES)
623
+ )
624
+ )
625
+ if weight is not None:
626
+ criticality.append(weight)
627
+ if identity is not None:
628
+ ids.append(identity)
629
+ if "critical" not in criticality:
630
+ issues.append("inventory.interactions must mark at least one interaction as 'critical'")
631
+
632
+
633
+ def _states(issues: list, value: JsonValue, routes: dict, ids: list) -> None:
634
+ for position, candidate in _objects(issues, value, "inventory.states"):
635
+ label = "inventory.states[{0}]".format(position)
636
+ state = _mapping(issues, candidate, STATE_KEYS, label)
637
+ if state is None:
638
+ continue
639
+ identity = _typed_identifier(issues, state.get("id"), "state", "{0}.id".format(label))
640
+ _resolve(issues, state.get("route_id"), "route", routes, "{0}.route_id".format(label))
641
+ _enum(issues, state.get("kind"), STATE_KINDS, "{0}.kind".format(label))
642
+ if identity is not None:
643
+ ids.append(identity)
644
+
645
+
646
+ def _viewports(issues: list, value: JsonValue, ids: list) -> None:
647
+ for position, candidate in _objects(issues, value, "inventory.viewports"):
648
+ label = "inventory.viewports[{0}]".format(position)
649
+ viewport = _mapping(issues, candidate, VIEWPORT_KEYS, label)
650
+ if viewport is None:
651
+ continue
652
+ identity = _typed_identifier(
653
+ issues, viewport.get("id"), "viewport", "{0}.id".format(label)
654
+ )
655
+ _enum(
656
+ issues, viewport.get("category"), VIEWPORT_CATEGORIES, "{0}.category".format(label)
657
+ )
658
+ _integer(issues, viewport.get("width_px"), "{0}.width_px".format(label), 1, 16384)
659
+ _integer(issues, viewport.get("height_px"), "{0}.height_px".format(label), 1, 16384)
660
+ if identity is not None:
661
+ ids.append(identity)
662
+
663
+
664
+ def _references(issues: list, value: JsonValue, ids: list) -> None:
665
+ for position, candidate in _objects(issues, value, "inventory.references"):
666
+ label = "inventory.references[{0}]".format(position)
667
+ reference = _mapping(issues, candidate, REFERENCE_KEYS, label)
668
+ if reference is None:
669
+ continue
670
+ identity = _typed_identifier(
671
+ issues, reference.get("id"), "reference", "{0}.id".format(label)
672
+ )
673
+ _enum(issues, reference.get("kind"), REFERENCE_KINDS, "{0}.kind".format(label))
674
+ _hash(issues, reference.get("sha256"), "{0}.sha256".format(label))
675
+ if identity is not None:
676
+ ids.append(identity)
677
+
678
+
679
+ def _authenticated_surfaces(issues: list, value: JsonValue, routes: dict) -> set:
680
+ covered = []
681
+ for position, candidate in _objects(issues, value, "inventory.authenticated_surfaces"):
682
+ label = "inventory.authenticated_surfaces[{0}]".format(position)
683
+ surface = _mapping(issues, candidate, SURFACE_KEYS, label)
684
+ if surface is None:
685
+ continue
686
+ route_id = _typed_identifier(
687
+ issues, surface.get("route_id"), "route", "{0}.route_id".format(label)
688
+ )
689
+ if route_id is not None:
690
+ if route_id in covered:
691
+ issues.append(
692
+ "{0}.route_id repeats a surface already declared: {1}".format(label, route_id)
693
+ )
694
+ else:
695
+ covered.append(route_id)
696
+ if route_id not in routes:
697
+ issues.append(
698
+ "{0}.route_id does not resolve to a declared route: {1}".format(
699
+ label, route_id
700
+ )
701
+ )
702
+ # Anything other than a literal true means the reviewer would be pointed at a
703
+ # real account, so it fails closed rather than warning.
704
+ if surface.get("safe_test_account") is not True:
705
+ issues.append("{0}.safe_test_account must be exactly true".format(label))
706
+ return set(covered)
707
+
708
+
709
+ def _inventory(issues: list, value: JsonValue, ids: list) -> None:
710
+ inventory = _mapping(issues, value, INVENTORY_KEYS, "inventory")
711
+ if inventory is None:
712
+ return
713
+ routes = _routes(issues, inventory.get("routes"), ids)
714
+ regions = _regions(issues, inventory.get("regions"), routes, ids)
715
+ _components(issues, inventory.get("components"), regions, ids)
716
+ _interactions(issues, inventory.get("interactions"), routes, ids)
717
+ _states(issues, inventory.get("states"), routes, ids)
718
+ _viewports(issues, inventory.get("viewports"), ids)
719
+ _references(issues, inventory.get("references"), ids)
720
+ covered = _authenticated_surfaces(issues, inventory.get("authenticated_surfaces"), routes)
721
+ for identity in sorted(routes):
722
+ if routes[identity] and identity not in covered:
723
+ issues.append(
724
+ "route {0} sets auth_required but no authenticated surface covers it".format(
725
+ identity
726
+ )
727
+ )
728
+ if not routes[identity] and identity in covered:
729
+ issues.append(
730
+ "route {0} is public but an authenticated surface claims it".format(identity)
731
+ )
732
+
733
+
734
+ def _declared_ids(contract: dict, category: str) -> set:
735
+ inventory = contract.get("inventory")
736
+ if not isinstance(inventory, dict) or not isinstance(inventory.get(category), list):
737
+ return set()
738
+ return {
739
+ item.get("id") for item in inventory[category]
740
+ if isinstance(item, dict) and isinstance(item.get("id"), str)
741
+ }
742
+
743
+
744
+ def _beta_references(issues: list, value: JsonValue, label: str, prefix: str, declared: set) -> None:
745
+ for position, candidate in _objects(issues, value, label, minimum=1):
746
+ identity = _typed_identifier(
747
+ issues, candidate, prefix, "{0}[{1}]".format(label, position)
748
+ )
749
+ if identity is not None and identity not in declared:
750
+ issues.append("{0} is not a declared {1}".format(identity, prefix))
751
+ if isinstance(value, list) and len(set(item for item in value if isinstance(item, str))) != len(value):
752
+ issues.append("{0} entries must be unique".format(label))
753
+
754
+
755
+ def _beta_extension(issues: list, contract: dict, ids: list) -> None:
756
+ _enum(issues, contract.get("lane"), LANES, "lane")
757
+ routes = _declared_ids(contract, "routes")
758
+ components = _declared_ids(contract, "components")
759
+ interactions = _declared_ids(contract, "interactions")
760
+ states = _declared_ids(contract, "states")
761
+ viewports = _declared_ids(contract, "viewports")
762
+ inventory_ids = set().union(*(
763
+ _declared_ids(contract, category) for category in INVENTORY_KEYS
764
+ ))
765
+
766
+ for position, candidate in _objects(issues, contract.get("tokens"), "tokens", minimum=1):
767
+ label = "tokens[{0}]".format(position)
768
+ token = _mapping(issues, candidate, ("id", "category", "value", "usage"), label)
769
+ if token is None:
770
+ continue
771
+ identity = _typed_identifier(issues, token.get("id"), "token", "{0}.id".format(label))
772
+ if identity is not None:
773
+ ids.append(identity)
774
+ _enum(issues, token.get("category"), TOKEN_CATEGORIES, "{0}.category".format(label))
775
+ _text(
776
+ issues, token.get("value"), "{0}.value".format(label), MAX_BETA_TEXT_CHARACTERS
777
+ )
778
+ _text(
779
+ issues, token.get("usage"), "{0}.usage".format(label), MAX_BETA_TEXT_CHARACTERS
780
+ )
781
+
782
+ behavior_fields = ("component_id", "state_ids", "interaction_ids", "keyboard_behavior")
783
+ for position, candidate in _objects(
784
+ issues, contract.get("component_behaviors"), "component_behaviors", minimum=1
785
+ ):
786
+ label = "component_behaviors[{0}]".format(position)
787
+ behavior = _mapping(issues, candidate, behavior_fields, label)
788
+ if behavior is None:
789
+ continue
790
+ component_id = _typed_identifier(
791
+ issues, behavior.get("component_id"), "component", "{0}.component_id".format(label)
792
+ )
793
+ if component_id is not None and component_id not in components:
794
+ issues.append("{0} is not a declared component".format(component_id))
795
+ _beta_references(
796
+ issues, behavior.get("state_ids"), "{0}.state_ids".format(label), "state", states
797
+ )
798
+ _beta_references(
799
+ issues, behavior.get("interaction_ids"), "{0}.interaction_ids".format(label),
800
+ "interaction", interactions,
801
+ )
802
+ _text(
803
+ issues,
804
+ behavior.get("keyboard_behavior"),
805
+ "{0}.keyboard_behavior".format(label),
806
+ MAX_BETA_TEXT_CHARACTERS,
807
+ )
808
+
809
+ transformation_fields = ("route_id", "viewport_id", "behavior")
810
+ for position, candidate in _objects(
811
+ issues, contract.get("responsive_transformations"), "responsive_transformations", minimum=1
812
+ ):
813
+ label = "responsive_transformations[{0}]".format(position)
814
+ transformation = _mapping(issues, candidate, transformation_fields, label)
815
+ if transformation is None:
816
+ continue
817
+ route_id = _typed_identifier(
818
+ issues, transformation.get("route_id"), "route", "{0}.route_id".format(label)
819
+ )
820
+ if route_id is not None and route_id not in routes:
821
+ issues.append("{0} is not a declared route".format(route_id))
822
+ viewport_id = _typed_identifier(
823
+ issues, transformation.get("viewport_id"), "viewport", "{0}.viewport_id".format(label)
824
+ )
825
+ if viewport_id is not None and viewport_id not in viewports:
826
+ issues.append("{0} is not a declared viewport".format(viewport_id))
827
+ _text(
828
+ issues,
829
+ transformation.get("behavior"),
830
+ "{0}.behavior".format(label),
831
+ MAX_BETA_TEXT_CHARACTERS,
832
+ )
833
+
834
+ motion = _mapping(
835
+ issues, contract.get("motion"), ("policy", "reduced_motion_behavior", "transitions"),
836
+ "motion",
837
+ )
838
+ if motion is not None:
839
+ policy = _enum(issues, motion.get("policy"), MOTION_POLICIES, "motion.policy")
840
+ _text(
841
+ issues,
842
+ motion.get("reduced_motion_behavior"),
843
+ "motion.reduced_motion_behavior",
844
+ MAX_BETA_TEXT_CHARACTERS,
845
+ )
846
+ transitions = _objects(issues, motion.get("transitions"), "motion.transitions")
847
+ if policy != "none" and len(transitions) == 0:
848
+ issues.append("motion.transitions must not be empty for an active policy")
849
+ if policy == "none" and len(transitions) > 0:
850
+ issues.append("motion.transitions must be empty when policy is none")
851
+ for position, candidate in transitions:
852
+ label = "motion.transitions[{0}]".format(position)
853
+ transition = _mapping(
854
+ issues, candidate, ("id", "interaction_id", "duration_ms", "easing"), label
855
+ )
856
+ if transition is None:
857
+ continue
858
+ identity = _typed_identifier(
859
+ issues, transition.get("id"), "transition", "{0}.id".format(label)
860
+ )
861
+ if identity is not None:
862
+ ids.append(identity)
863
+ interaction_id = _typed_identifier(
864
+ issues, transition.get("interaction_id"), "interaction",
865
+ "{0}.interaction_id".format(label),
866
+ )
867
+ if interaction_id is not None and interaction_id not in interactions:
868
+ issues.append("{0} is not a declared interaction".format(interaction_id))
869
+ _integer(issues, transition.get("duration_ms"), "{0}.duration_ms".format(label), 0, 10000)
870
+ _text(
871
+ issues,
872
+ transition.get("easing"),
873
+ "{0}.easing".format(label),
874
+ MAX_BETA_TEXT_CHARACTERS,
875
+ )
876
+
877
+ criterion_fields = ("id", "observable", "verification", "required", "inventory_ids")
878
+ for position, candidate in _objects(
879
+ issues, contract.get("acceptance_criteria"), "acceptance_criteria", minimum=1
880
+ ):
881
+ label = "acceptance_criteria[{0}]".format(position)
882
+ criterion = _mapping(issues, candidate, criterion_fields, label)
883
+ if criterion is None:
884
+ continue
885
+ identity = _typed_identifier(
886
+ issues, criterion.get("id"), "criterion", "{0}.id".format(label)
887
+ )
888
+ if identity is not None:
889
+ ids.append(identity)
890
+ _text(
891
+ issues,
892
+ criterion.get("observable"),
893
+ "{0}.observable".format(label),
894
+ MAX_BETA_TEXT_CHARACTERS,
895
+ )
896
+ _enum(
897
+ issues, criterion.get("verification"), VERIFICATION_METHODS,
898
+ "{0}.verification".format(label),
899
+ )
900
+ if criterion.get("required") is not True:
901
+ issues.append("{0}.required must be exactly true".format(label))
902
+ for covered in _strings(
903
+ issues, criterion.get("inventory_ids"), "{0}.inventory_ids".format(label)
904
+ ):
905
+ if covered not in inventory_ids:
906
+ issues.append("{0} is not declared in contract inventory".format(covered))
907
+
908
+
909
+ # One defect, one issue. A reviewer fixing a dial wants the dial named, not a list
910
+ # of every consequence of the same mistake. bool is rejected explicitly because it
911
+ # is a subclass of int in Python, so True would otherwise pass as the integer 1.
912
+ def _taste(issues: list, value: JsonValue) -> None:
913
+ if not isinstance(value, dict):
914
+ issues.append("taste must be an object declaring {0}".format(list(TASTE_DIALS)))
915
+ return
916
+ unknown = sorted(set(value) - set(TASTE_DIALS))
917
+ if unknown:
918
+ issues.append("taste has unapproved dials {0}".format(unknown))
919
+ return
920
+ for dial in TASTE_DIALS:
921
+ label = "taste.{0}".format(dial)
922
+ if dial not in value:
923
+ issues.append("{0} is required whenever taste is declared".format(label))
924
+ return
925
+ reading = value[dial]
926
+ if isinstance(reading, bool) or not isinstance(reading, int) \
927
+ or reading < TASTE_LOW or reading > TASTE_HIGH:
928
+ issues.append(
929
+ "{0} must be an integer from {1} through {2}".format(label, TASTE_LOW, TASTE_HIGH)
930
+ )
931
+ return
932
+
933
+
934
+ def validate_design_contract(value: JsonValue) -> list:
935
+ """Return every rule violation found. An empty list means the contract is valid."""
936
+ issues = []
937
+ beta2 = isinstance(value, dict) and value.get("schema_id") == BETA2_SCHEMA_ID
938
+ beta = beta2 or (isinstance(value, dict) and value.get("schema_id") == BETA_SCHEMA_ID)
939
+ contract = _mapping(
940
+ issues,
941
+ value,
942
+ BETA_TOP_FIELDS if beta else TOP_KEYS,
943
+ "contract",
944
+ optional=BETA2_OPTIONAL_FIELDS if beta2 else (),
945
+ )
946
+ if contract is None:
947
+ return issues
948
+ if contract.get("schema_id") not in (SCHEMA_ID, BETA_SCHEMA_ID, BETA2_SCHEMA_ID):
949
+ issues.append(
950
+ "schema_id must equal '{0}', '{1}' or '{2}'".format(
951
+ SCHEMA_ID, BETA_SCHEMA_ID, BETA2_SCHEMA_ID
952
+ )
953
+ )
954
+ ids = []
955
+ identity = _typed_identifier(issues, contract.get("contract_id"), "contract", "contract_id")
956
+ if identity is not None:
957
+ ids.append(identity)
958
+ _hash(issues, contract.get("source_hash"), "source_hash")
959
+ _intent(issues, contract.get("intent"))
960
+ _direction(issues, contract.get("direction"))
961
+ _inventory(issues, contract.get("inventory"), ids)
962
+ _accessibility(issues, contract.get("accessibility"))
963
+ _localization(issues, contract.get("localization"))
964
+ _performance(issues, contract.get("performance"))
965
+ _evidence_policy(issues, contract.get("evidence_policy"))
966
+ ids.extend(_disclosures(issues, contract.get("omissions"), "omissions"))
967
+ ids.extend(_disclosures(issues, contract.get("accepted_exceptions"), "accepted_exceptions"))
968
+ if beta:
969
+ if "taste" in contract:
970
+ _taste(issues, contract.get("taste"))
971
+ _beta_extension(issues, contract, ids)
972
+ repeated = sorted(name for name, count in Counter(ids).items() if count > 1)
973
+ if repeated:
974
+ issues.append("ids must be unique across the whole contract; repeated {0}".format(repeated))
975
+ return issues
976
+
977
+
978
+ def design_contract_report(value: JsonValue) -> dict:
979
+ issues = validate_design_contract(value)
980
+ beta2 = isinstance(value, dict) and value.get("schema_id") == BETA2_SCHEMA_ID
981
+ beta = beta2 or (isinstance(value, dict) and value.get("schema_id") == BETA_SCHEMA_ID)
982
+ alpha = isinstance(value, dict) and value.get("schema_id") == SCHEMA_ID
983
+ valid = not issues
984
+ if beta2:
985
+ schema = BETA2_SCHEMA_ID
986
+ elif beta:
987
+ schema = BETA_SCHEMA_ID
988
+ else:
989
+ schema = SCHEMA_ID
990
+ return {
991
+ "diagnostics": ["LEGACY_SCHEMA_V1ALPHA1"] if alpha else [],
992
+ "evidence_eligible": beta and valid,
993
+ "issues": issues,
994
+ "schema": schema,
995
+ "valid": valid,
996
+ }