hyperweave 0.2.2__tar.gz → 0.2.4__tar.gz

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 (336) hide show
  1. {hyperweave-0.2.2 → hyperweave-0.2.4}/CHANGELOG.md +44 -0
  2. {hyperweave-0.2.2 → hyperweave-0.2.4}/PKG-INFO +3 -3
  3. {hyperweave-0.2.2 → hyperweave-0.2.4}/README.md +2 -2
  4. {hyperweave-0.2.2 → hyperweave-0.2.4}/pyproject.toml +1 -0
  5. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/_version.py +2 -2
  6. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/compose/context.py +3 -0
  7. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/compose/engine.py +12 -0
  8. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/compose/resolver.py +49 -7
  9. hyperweave-0.2.4/src/hyperweave/compose/resolvers/chart.py +139 -0
  10. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/compose/resolvers/stats.py +12 -5
  11. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/connectors/base.py +3 -1
  12. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/connectors/github.py +31 -0
  13. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/core/schema.py +13 -0
  14. hyperweave-0.2.4/src/hyperweave/core/state.py +45 -0
  15. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/genomes/chrome-horizon.json +4 -0
  16. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/profiles/chrome.yaml +2 -2
  17. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/kit.py +2 -30
  18. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/render/chart_engine.py +220 -15
  19. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/components/metadata.svg.j2 +4 -4
  20. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/badge/chrome-defs.j2 +8 -3
  21. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/chart/brutalist-content.j2 +10 -14
  22. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/chart/chrome-content.j2 +10 -13
  23. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/stats/chrome-content.j2 +12 -15
  24. hyperweave-0.2.4/src/hyperweave/templates/frames/strip/chrome-content.j2 +29 -0
  25. hyperweave-0.2.4/src/hyperweave/templates/frames/strip/chrome-defs.j2 +67 -0
  26. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/strip.svg.j2 +12 -5
  27. {hyperweave-0.2.2 → hyperweave-0.2.4}/tests/test_chart_engine.py +145 -1
  28. {hyperweave-0.2.2 → hyperweave-0.2.4}/tests/test_chart_frame.py +111 -3
  29. {hyperweave-0.2.2 → hyperweave-0.2.4}/tests/test_github_scrape.py +8 -8
  30. {hyperweave-0.2.2 → hyperweave-0.2.4}/tests/test_kit.py +23 -22
  31. hyperweave-0.2.4/tests/test_serve_live_state.py +82 -0
  32. hyperweave-0.2.4/tests/test_state_inference.py +79 -0
  33. {hyperweave-0.2.2 → hyperweave-0.2.4}/tests/test_stats_card.py +27 -0
  34. hyperweave-0.2.2/src/hyperweave/compose/resolvers/chart.py +0 -125
  35. hyperweave-0.2.2/src/hyperweave/templates/frames/strip/chrome-content.j2 +0 -20
  36. hyperweave-0.2.2/src/hyperweave/templates/frames/strip/chrome-defs.j2 +0 -85
  37. {hyperweave-0.2.2 → hyperweave-0.2.4}/.dockerignore +0 -0
  38. {hyperweave-0.2.2 → hyperweave-0.2.4}/.github/workflows/ci.yml +0 -0
  39. {hyperweave-0.2.2 → hyperweave-0.2.4}/.github/workflows/deploy.yml +0 -0
  40. {hyperweave-0.2.2 → hyperweave-0.2.4}/.github/workflows/publish.yml +0 -0
  41. {hyperweave-0.2.2 → hyperweave-0.2.4}/.gitignore +0 -0
  42. {hyperweave-0.2.2 → hyperweave-0.2.4}/Dockerfile +0 -0
  43. {hyperweave-0.2.2 → hyperweave-0.2.4}/LICENSE +0 -0
  44. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/buttons/button-liquid.svg +0 -0
  45. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/cards/card-butterfly.svg +0 -0
  46. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/cards/card-python.svg +0 -0
  47. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/cards/card-sunflower.svg +0 -0
  48. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/cards/card-waves.svg +0 -0
  49. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/badge_critical.svg +0 -0
  50. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/badge_passing.svg +0 -0
  51. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/badge_warning.svg +0 -0
  52. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/banner.svg +0 -0
  53. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/icons/discord.svg +0 -0
  54. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/icons/notion.svg +0 -0
  55. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/icons/reddit.svg +0 -0
  56. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/icons/spotify.svg +0 -0
  57. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/icons/youtube.svg +0 -0
  58. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/marquee_counter.svg +0 -0
  59. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/marquee_horizontal.svg +0 -0
  60. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/marquee_vertical.svg +0 -0
  61. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/profile-cards/chart_stars_full.svg +0 -0
  62. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/profile-cards/stats.svg +0 -0
  63. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/brutalist-emerald/strip.svg +0 -0
  64. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/chrome-horizon/badge_critical.svg +0 -0
  65. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/chrome-horizon/badge_passing.svg +0 -0
  66. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/chrome-horizon/badge_warning.svg +0 -0
  67. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/chrome-horizon/banner.svg +0 -0
  68. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/chrome-horizon/icons/bluesky.svg +0 -0
  69. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/chrome-horizon/icons/github.svg +0 -0
  70. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/chrome-horizon/icons/instagram.svg +0 -0
  71. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/chrome-horizon/icons/mastodon.svg +0 -0
  72. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/chrome-horizon/icons/x.svg +0 -0
  73. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/chrome-horizon/marquee_counter.svg +0 -0
  74. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/chrome-horizon/marquee_horizontal.svg +0 -0
  75. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/chrome-horizon/marquee_vertical.svg +0 -0
  76. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/chrome-horizon/profile-cards/chart_stars_full.svg +0 -0
  77. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/chrome-horizon/profile-cards/stats.svg +0 -0
  78. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/chrome-horizon/strip.svg +0 -0
  79. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/telemetry/master_card.svg +0 -0
  80. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/telemetry/receipt.svg +0 -0
  81. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/examples/telemetry/rhythm_strip.svg +0 -0
  82. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/footers/inneraura-footer-liquid.svg +0 -0
  83. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/footers/inneraura-footer-purple.svg +0 -0
  84. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/footers/inneraura-footer.svg +0 -0
  85. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/hyperweave-banner.svg +0 -0
  86. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/icons/cobalt-sapphire-discord.svg +0 -0
  87. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/icons/cobalt-sapphire-docs.svg +0 -0
  88. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/icons/cobalt-sapphire-github.svg +0 -0
  89. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/icons/cobalt-sapphire-instagram.svg +0 -0
  90. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/icons/cobalt-sapphire-linkedin.svg +0 -0
  91. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/icons/cobalt-sapphire-tiktok.svg +0 -0
  92. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/icons/cobalt-sapphire-x.svg +0 -0
  93. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/icons/cobalt-sapphire-youtube.svg +0 -0
  94. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/marquees/badge-showcase-triple.svg +0 -0
  95. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/marquees/genome-marquee-triple.svg +0 -0
  96. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/marquees/sample-badges.svg +0 -0
  97. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/mintlify-assets/404.svg +0 -0
  98. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/mintlify-assets/callout-icons.svg +0 -0
  99. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/mintlify-assets/divider.svg +0 -0
  100. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/mintlify-assets/favicon.svg +0 -0
  101. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/mintlify-assets/hero-banner.svg +0 -0
  102. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/mintlify-assets/hyperweave-banner-v2.svg +0 -0
  103. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/mintlify-assets/hyperweave-navbar-logo.svg +0 -0
  104. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/mintlify-assets/loader.svg +0 -0
  105. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/mintlify-assets/og-image.svg +0 -0
  106. {hyperweave-0.2.2 → hyperweave-0.2.4}/assets/timelines/hyperweave-roadmap.svg +0 -0
  107. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/assets/favicon.svg +0 -0
  108. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/assets/hyperweave-banner.svg +0 -0
  109. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/assets/hyperweave-navbar-logo.svg +0 -0
  110. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/assets/og-image.svg +0 -0
  111. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/explanation/architecture.mdx +0 -0
  112. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/explanation/camo-compatibility.mdx +0 -0
  113. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/explanation/cim-compliance.mdx +0 -0
  114. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/explanation/genome-profile-system.mdx +0 -0
  115. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/explanation/metadata-tiers.mdx +0 -0
  116. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/how-to/add-motion-to-badges.mdx +0 -0
  117. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/how-to/create-session-receipts.mdx +0 -0
  118. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/how-to/set-up-claude-code-hooks.mdx +0 -0
  119. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/how-to/use-live-data-badges.mdx +0 -0
  120. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/introduction.mdx +0 -0
  121. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/mint.json +0 -0
  122. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/quickstart.mdx +0 -0
  123. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/reference/cli.mdx +0 -0
  124. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/reference/compose-spec.mdx +0 -0
  125. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/reference/genomes.mdx +0 -0
  126. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/reference/http-api.mdx +0 -0
  127. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/reference/mcp-tools.mdx +0 -0
  128. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/reference/motions.mdx +0 -0
  129. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/reference/telemetry-contract.mdx +0 -0
  130. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/spec/hyperweave-protocol.mdx +0 -0
  131. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/tutorials/build-an-artifact-kit.mdx +0 -0
  132. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/tutorials/compose-your-first-badge.mdx +0 -0
  133. {hyperweave-0.2.2 → hyperweave-0.2.4}/docs/tutorials/install-session-telemetry.mdx +0 -0
  134. {hyperweave-0.2.2 → hyperweave-0.2.4}/fly.toml +0 -0
  135. {hyperweave-0.2.2 → hyperweave-0.2.4}/hooks/install.py +0 -0
  136. {hyperweave-0.2.2 → hyperweave-0.2.4}/hooks/session_end.sh +0 -0
  137. {hyperweave-0.2.2 → hyperweave-0.2.4}/justfile +0 -0
  138. {hyperweave-0.2.2 → hyperweave-0.2.4}/scripts/extract_glyphs.py +0 -0
  139. {hyperweave-0.2.2 → hyperweave-0.2.4}/scripts/generate_hw_compliant.py +0 -0
  140. {hyperweave-0.2.2 → hyperweave-0.2.4}/scripts/generate_proofset.py +0 -0
  141. {hyperweave-0.2.2 → hyperweave-0.2.4}/scripts/stress_test.py +0 -0
  142. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/__init__.py +0 -0
  143. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/__main__.py +0 -0
  144. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/cli.py +0 -0
  145. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/compose/__init__.py +0 -0
  146. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/compose/assembler.py +0 -0
  147. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/compose/lanes.py +0 -0
  148. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/compose/resolvers/__init__.py +0 -0
  149. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/compose/resolvers/timeline.py +0 -0
  150. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/config/__init__.py +0 -0
  151. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/config/genome_validator.py +0 -0
  152. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/config/loader.py +0 -0
  153. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/config/registry.py +0 -0
  154. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/config/settings.py +0 -0
  155. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/connectors/__init__.py +0 -0
  156. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/connectors/arxiv.py +0 -0
  157. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/connectors/cache.py +0 -0
  158. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/connectors/rest.py +0 -0
  159. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/core/__init__.py +0 -0
  160. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/core/color.py +0 -0
  161. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/core/contracts.py +0 -0
  162. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/core/enums.py +0 -0
  163. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/core/models.py +0 -0
  164. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/core/text.py +0 -0
  165. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/core/thresholds.py +0 -0
  166. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/css/accessibility.css +0 -0
  167. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/css/bridge.css +0 -0
  168. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/css/expression.css +0 -0
  169. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/css/phase-colors.css +0 -0
  170. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/css/status.css +0 -0
  171. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/css/telemetry.css +0 -0
  172. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/font-metrics/inter.json +0 -0
  173. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/fonts/jetbrains-mono.b64 +0 -0
  174. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/fonts/jetbrains-mono.meta.json +0 -0
  175. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/fonts/orbitron.b64 +0 -0
  176. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/fonts/orbitron.meta.json +0 -0
  177. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/genomes/brutalist-emerald.json +0 -0
  178. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/genomes/telemetry-void.json +0 -0
  179. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/glyphs.json +0 -0
  180. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/motions/border/chromatic-pulse.yaml +0 -0
  181. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/motions/border/corner-trace.yaml +0 -0
  182. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/motions/border/dual-orbit.yaml +0 -0
  183. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/motions/border/entanglement.yaml +0 -0
  184. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/motions/border/rimrun.yaml +0 -0
  185. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/motions/kinetic/bars.yaml +0 -0
  186. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/motions/kinetic/breach.yaml +0 -0
  187. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/motions/kinetic/broadcast.yaml +0 -0
  188. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/motions/kinetic/cascade.yaml +0 -0
  189. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/motions/kinetic/collapse.yaml +0 -0
  190. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/motions/kinetic/converge.yaml +0 -0
  191. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/motions/kinetic/crash.yaml +0 -0
  192. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/motions/kinetic/drop.yaml +0 -0
  193. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/motions/kinetic/pulse.yaml +0 -0
  194. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/motions/static.yaml +0 -0
  195. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/policies/normal.json +0 -0
  196. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/policies/permissive.json +0 -0
  197. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/policies/ungoverned.json +0 -0
  198. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/profiles/brutalist.contract.json +0 -0
  199. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/profiles/brutalist.yaml +0 -0
  200. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/profiles/chrome.contract.json +0 -0
  201. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/specimens.yaml +0 -0
  202. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/telemetry/model-pricing.yaml +0 -0
  203. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/telemetry/stage-config.yaml +0 -0
  204. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/telemetry/stage-labels.yaml +0 -0
  205. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/telemetry/tool-classes.yaml +0 -0
  206. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/telemetry/tool-colors.yaml +0 -0
  207. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/terminals/arrow.json +0 -0
  208. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/terminals/aurora.json +0 -0
  209. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/terminals/beacon.json +0 -0
  210. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/terminals/bracket3d.json +0 -0
  211. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/terminals/chevron.json +0 -0
  212. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/terminals/crosshair.json +0 -0
  213. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/terminals/diamond.json +0 -0
  214. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/terminals/rocket.json +0 -0
  215. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/terminals/rules/bar.json +0 -0
  216. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/terminals/rules/dashed.json +0 -0
  217. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/terminals/rules/five-wave.json +0 -0
  218. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/terminals/rules/spectral.json +0 -0
  219. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/terminals/rules/straight.json +0 -0
  220. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/terminals/rules/wave.json +0 -0
  221. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/data/terminals/stijl.json +0 -0
  222. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/mcp/__init__.py +0 -0
  223. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/mcp/__main__.py +0 -0
  224. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/mcp/server.py +0 -0
  225. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/py.typed +0 -0
  226. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/render/__init__.py +0 -0
  227. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/render/fonts.py +0 -0
  228. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/render/glyphs.py +0 -0
  229. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/render/motion.py +0 -0
  230. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/render/templates.py +0 -0
  231. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/serve/__init__.py +0 -0
  232. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/serve/app.py +0 -0
  233. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/telemetry/__init__.py +0 -0
  234. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/telemetry/capture.py +0 -0
  235. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/telemetry/contract.py +0 -0
  236. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/telemetry/corrections.py +0 -0
  237. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/telemetry/cost.py +0 -0
  238. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/telemetry/models.py +0 -0
  239. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/telemetry/parser.py +0 -0
  240. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/telemetry/stages.py +0 -0
  241. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/components/filter.svg.j2 +0 -0
  242. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/components/glyph-inline.svg.j2 +0 -0
  243. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/components/glyph.svg.j2 +0 -0
  244. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/components/gradient.svg.j2 +0 -0
  245. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/components/metric.svg.j2 +0 -0
  246. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/components/phase-segment.svg.j2 +0 -0
  247. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/components/rule.svg.j2 +0 -0
  248. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/components/status.svg.j2 +0 -0
  249. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/components/terminal.svg.j2 +0 -0
  250. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/components/treemap.svg.j2 +0 -0
  251. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/document.svg.j2 +0 -0
  252. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/error-badge.svg.j2 +0 -0
  253. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/badge/brutalist-content.j2 +0 -0
  254. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/badge/brutalist-defs.j2 +0 -0
  255. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/badge/chrome-content.j2 +0 -0
  256. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/badge/default-content.j2 +0 -0
  257. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/badge/default-defs.j2 +0 -0
  258. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/badge.svg.j2 +0 -0
  259. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/banner/brutalist-defs.j2 +0 -0
  260. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/banner/chrome-defs.j2 +0 -0
  261. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/banner/default-defs.j2 +0 -0
  262. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/banner.svg.j2 +0 -0
  263. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/catalog.svg.j2 +0 -0
  264. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/chart/brutalist-defs.j2 +0 -0
  265. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/chart/chrome-defs.j2 +0 -0
  266. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/chart.svg.j2 +0 -0
  267. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/divider.svg.j2 +0 -0
  268. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/icon/brutalist-content.j2 +0 -0
  269. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/icon/brutalist-defs.j2 +0 -0
  270. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/icon/chrome-content.j2 +0 -0
  271. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/icon/chrome-defs.j2 +0 -0
  272. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/icon/default-content.j2 +0 -0
  273. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/icon/default-defs.j2 +0 -0
  274. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/icon.svg.j2 +0 -0
  275. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-counter/brutalist-content.j2 +0 -0
  276. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-counter/brutalist-defs.j2 +0 -0
  277. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-counter/chrome-content.j2 +0 -0
  278. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-counter/chrome-defs.j2 +0 -0
  279. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-counter.svg.j2 +0 -0
  280. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-horizontal/brutalist-content.j2 +0 -0
  281. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-horizontal/brutalist-defs.j2 +0 -0
  282. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-horizontal/chrome-content.j2 +0 -0
  283. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-horizontal/chrome-defs.j2 +0 -0
  284. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-horizontal.svg.j2 +0 -0
  285. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-vertical/brutalist-content.j2 +0 -0
  286. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-vertical/brutalist-defs.j2 +0 -0
  287. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-vertical/chrome-content.j2 +0 -0
  288. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-vertical/chrome-defs.j2 +0 -0
  289. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/marquee-vertical.svg.j2 +0 -0
  290. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/master-card.svg.j2 +0 -0
  291. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/receipt.svg.j2 +0 -0
  292. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/rhythm-strip.svg.j2 +0 -0
  293. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/stats/brutalist-content.j2 +0 -0
  294. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/stats/brutalist-defs.j2 +0 -0
  295. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/stats/chrome-defs.j2 +0 -0
  296. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/stats.svg.j2 +0 -0
  297. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/strip/brutalist-content.j2 +0 -0
  298. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/strip/brutalist-defs.j2 +0 -0
  299. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/strip/brutalist-status.j2 +0 -0
  300. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/strip/chrome-status.j2 +0 -0
  301. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/strip/default-content.j2 +0 -0
  302. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/strip/default-defs.j2 +0 -0
  303. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/strip/default-status.j2 +0 -0
  304. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/timeline/default-content.j2 +0 -0
  305. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/frames/timeline.svg.j2 +0 -0
  306. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/motions/border/chromatic-pulse.svg.j2 +0 -0
  307. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/motions/border/corner-trace.svg.j2 +0 -0
  308. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/motions/border/dual-orbit.svg.j2 +0 -0
  309. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/motions/border/entanglement.svg.j2 +0 -0
  310. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/motions/border/rimrun.svg.j2 +0 -0
  311. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/motions/kinetic/bars.svg.j2 +0 -0
  312. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/motions/kinetic/breach.svg.j2 +0 -0
  313. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/motions/kinetic/broadcast.svg.j2 +0 -0
  314. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/motions/kinetic/cascade.svg.j2 +0 -0
  315. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/motions/kinetic/collapse.svg.j2 +0 -0
  316. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/motions/kinetic/converge.svg.j2 +0 -0
  317. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/motions/kinetic/crash.svg.j2 +0 -0
  318. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/motions/kinetic/drop.svg.j2 +0 -0
  319. {hyperweave-0.2.2 → hyperweave-0.2.4}/src/hyperweave/templates/motions/kinetic/pulse.svg.j2 +0 -0
  320. {hyperweave-0.2.2 → hyperweave-0.2.4}/tests/__init__.py +0 -0
  321. {hyperweave-0.2.2 → hyperweave-0.2.4}/tests/conftest.py +0 -0
  322. {hyperweave-0.2.2 → hyperweave-0.2.4}/tests/fixtures/github_contributions/synthetic.html +0 -0
  323. {hyperweave-0.2.2 → hyperweave-0.2.4}/tests/fixtures/session.json +0 -0
  324. {hyperweave-0.2.2 → hyperweave-0.2.4}/tests/fixtures/session.jsonl +0 -0
  325. {hyperweave-0.2.2 → hyperweave-0.2.4}/tests/test_assembler.py +0 -0
  326. {hyperweave-0.2.2 → hyperweave-0.2.4}/tests/test_connectors.py +0 -0
  327. {hyperweave-0.2.2 → hyperweave-0.2.4}/tests/test_core.py +0 -0
  328. {hyperweave-0.2.2 → hyperweave-0.2.4}/tests/test_mcp.py +0 -0
  329. {hyperweave-0.2.2 → hyperweave-0.2.4}/tests/test_paradigm_dispatch.py +0 -0
  330. {hyperweave-0.2.2 → hyperweave-0.2.4}/tests/test_proofset.py +0 -0
  331. {hyperweave-0.2.2 → hyperweave-0.2.4}/tests/test_render.py +0 -0
  332. {hyperweave-0.2.2 → hyperweave-0.2.4}/tests/test_serve.py +0 -0
  333. {hyperweave-0.2.2 → hyperweave-0.2.4}/tests/test_telemetry.py +0 -0
  334. {hyperweave-0.2.2 → hyperweave-0.2.4}/tests/test_telemetry_integration.py +0 -0
  335. {hyperweave-0.2.2 → hyperweave-0.2.4}/tests/test_timeline.py +0 -0
  336. {hyperweave-0.2.2 → hyperweave-0.2.4}/uv.lock +0 -0
@@ -5,6 +5,50 @@ All notable changes to HyperWeave are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.2.4] - 2026-04-13
9
+
10
+ ### Added
11
+
12
+ - **Truthful chart rendering.** Charts now reflect what the data actually says. When a GitHub fetch fails, the chart renders a clear `DATA UNAVAILABLE` overlay instead of a synthesized placeholder curve. Brand-new repos with zero stars render `NEW REPO · NO STARS YET`. Every chart carries a status attribute (`fresh`, `stale`, `empty`) that downstream consumers can read.
13
+ - **Adaptive chart axes.** Y-tick values auto-generate from the data range using round numbers (1, 2, 5 × powers of 10), so labels agree with the curve at any scale. X-axis year labels come from actual point timestamps — the old hardcoded `EARLY '24` / `LATE '24` placeholders are gone.
14
+ - **Single-page stargazer granularity.** Repos with ≤30 stars now render with individual stargazer timestamps instead of collapsed samples, so early-stage projects produce visible curves instead of flat lines.
15
+ - **Adaptive strip metric cell pitch.** Strip cells size themselves to fit the widest metric, then propagate that pitch uniformly. Long values no longer overflow; the grid stays balanced.
16
+
17
+ ### Fixed
18
+
19
+ - **Live state now reflects live data.** Badges, strips, and live routes across HTTP, CLI, and MCP auto-detect pass/fail/warning/critical state from fetched values. Previously this inference ran only inside `hw kit readme`, so a live badge fetching `build=failing` rendered as green ("active") instead of red. Explicit overrides (`?state=passing`, `--state failing`, MCP `state` argument) continue to win.
20
+ - **Generated SVGs advertised the wrong version in their embedded metadata.** The `version` variable that feeds `<hw:artifact version="...">`, `<hw:generator>`, `<hw:genome>`, and `<dc:creator>` was never populated, so every SVG from v0.2.0–v0.2.3 declared itself as `0.1.0` regardless of installed version. The metadata now reflects the real release.
21
+ - **Stats card activity bars blurred on mobile.** Bars now render with a solid fill and pixel-crisp edges rather than a multi-stop vertical gradient. The icy highlight lives in the horizon shelf-glow above the bars, so small-viewport rendering stays sharp.
22
+ - **Strip chrome typography drifted from badge.** Chrome strip metric values render in Orbitron 17px upright, matching the chrome badge — replacing the previous Impact-italic treatment. Chrome strip and chrome badge now share one typographic system.
23
+ - **Strip chrome filter stack caused mobile blur.** Removed specular lighting and the text-shadow filter, both of which rasterized poorly on small frames. Replaced the sheen with vector hairlines so highlights stay pixel-perfect at every size.
24
+ - **Chart axis labels no longer hardcoded.** Both brutalist and chrome chart templates read axis labels from the chart engine; changing the data range updates the labels automatically.
25
+ - **User-Agent header reported the wrong version.** Outbound HTTP requests identify as the installed HyperWeave version rather than a stale `0.1.0`.
26
+
27
+ ### Changed
28
+
29
+ - **Chart resolver is a three-state machine.** `stale` (fetch failed), `empty` (zero-value repo), or `fresh` (real data). The previous behavior of synthesizing a placeholder curve on failure is removed — data truthfulness is now a rendering contract.
30
+ - **Strip skew is profile-declared.** The hardcoded italic skew on chrome strips has been removed; profiles that want a skew opt in explicitly via a profile field.
31
+
32
+ ### Known follow-ups (not blocking v0.2.4)
33
+
34
+ - **Chart hero strip placeholders.** The static repo slug and date-range label on charts are not yet data-driven; only the curve and axes are.
35
+ - **Chart/stats data-provenance states lack dedicated styling.** Charts with failed fetches render a `stale` status, but no CSS rule matches it — the visual is covered by the text overlay rather than a distinct chrome color. Same for `empty`.
36
+ - **`loop` artifact status is declared but unused.** The status enum includes `loop`, exposed via MCP schema, but no inference logic produces it and no CSS animation exists for it.
37
+
38
+ ## [0.2.3] - 2026-04-13
39
+
40
+ ### Added
41
+
42
+ - **Genome-level `text_metrics` field.** Genomes can now declare per-zone text width multipliers (`badge_label_width_factor`, `badge_value_width_factor`) so the resolver sizes frames correctly for non-default display fonts. Defaults preserve pre-v0.2.3 behavior; new fonts (e.g. Orbitron on chrome-horizon) opt in without touching the resolver. Extensible to future zones without a schema change.
43
+ - chrome-horizon ships `badge_value_width_factor = 1.35` to match Orbitron 900 glyph advances at the compositor badge scale.
44
+
45
+ ### Fixed
46
+
47
+ - **Orbitron was not actually loading on badges and strips.** The v0.2.0 font bundler emits `@font-face` via a `{{ font_faces | safe }}` Jinja variable, but that variable was only rendered in `stats/chrome-defs.j2` and `chart/chrome-defs.j2` — badge and strip chrome-defs templates omitted it. As a result, v0.2.1's switch to `var(--dna-font-display)` fell through to the system-ui fallback instead of rendering Orbitron. `{{ font_faces | safe }}` is now emitted in `badge/chrome-defs.j2` and `strip/chrome-defs.j2` so the bundled WOFF2 actually loads.
48
+ - **chrome-horizon badge typography overflow.** Compounded by the font-loading bug above, v0.2.1's badge font sizes (11/17 label/value) matched the magazine's 200x52 showcase badge but the compositor badge is 125x22 (~40% of magazine scale). Scaled to 8/11 and combined with the new `text_metrics` width factor so the value text and status diamond no longer collide.
49
+ - **Activity bar vector halos produced visible "fat bar" artifacts on mobile.** The v0.2.1 fix replaced `feGaussianBlur` with 2-layer sibling-rect halos, but those expanded the visual width of each 7px bar by 4px total, which read as blurry on small viewports. The magazine specimen's light-cyan top highlight is carried by the `ch-bar` gradient's first stop (#C8DAE6) alone, not by any halo — so the halos are removed entirely. Bars render as crisp gradient rects at every scale.
50
+ - `tier2/` added to ruff `extend-exclude` so `just fmt` no longer trips on internal research files.
51
+
8
52
  ## [0.2.2] - 2026-04-13
9
53
 
10
54
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperweave
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Headless visual output layer for AI agents. One API call → self-contained SVG.
5
5
  Project-URL: Repository, https://github.com/InnerAura/hyperweave
6
6
  Project-URL: Issues, https://github.com/InnerAura/hyperweave/issues
@@ -325,8 +325,8 @@ Every artifact ships with:
325
325
  |---|---|
326
326
  | Frame types | 15 (badge, strip, banner, icon, divider, marquee-h/v/counter, receipt, rhythm-strip, master-card, catalog, stats, chart, timeline) |
327
327
  | Genomes | 2 (brutalist-emerald, chrome-horizon) |
328
- | Motion configs | 16 (1 static + 5 border SMIL + 10 kinetic CSS) |
329
- | Glyphs | 97 (91 Simple Icons + 6 geometric) |
328
+ | Motion configs | 15 (1 static + 5 border SMIL + 9 kinetic CSS) |
329
+ | Glyphs | 99 (93 Simple Icons + 6 geometric) |
330
330
  | Divider variants | 5 (block, current, takeoff, void, zeropoint) |
331
331
  | Metadata tiers | 5 (Tier 0 silent &rarr; Tier 4 reasoning) |
332
332
  | Paradigms | 3 per frame (default, brutalist, chrome) — per-frame dispatch from genome |
@@ -293,8 +293,8 @@ Every artifact ships with:
293
293
  |---|---|
294
294
  | Frame types | 15 (badge, strip, banner, icon, divider, marquee-h/v/counter, receipt, rhythm-strip, master-card, catalog, stats, chart, timeline) |
295
295
  | Genomes | 2 (brutalist-emerald, chrome-horizon) |
296
- | Motion configs | 16 (1 static + 5 border SMIL + 10 kinetic CSS) |
297
- | Glyphs | 97 (91 Simple Icons + 6 geometric) |
296
+ | Motion configs | 15 (1 static + 5 border SMIL + 9 kinetic CSS) |
297
+ | Glyphs | 99 (93 Simple Icons + 6 geometric) |
298
298
  | Divider variants | 5 (block, current, takeoff, void, zeropoint) |
299
299
  | Metadata tiers | 5 (Tier 0 silent &rarr; Tier 4 reasoning) |
300
300
  | Paradigms | 3 per frame (default, brutalist, chrome) — per-frame dispatch from genome |
@@ -94,6 +94,7 @@ markers = [
94
94
  line-length = 120
95
95
  target-version = "py312"
96
96
  src = ["src"]
97
+ extend-exclude = ["tier2"]
97
98
 
98
99
  [tool.ruff.lint]
99
100
  select = ["E", "F", "I", "UP", "B", "SIM", "TCH", "RUF"]
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.2.2'
22
- __version_tuple__ = version_tuple = (0, 2, 2)
21
+ __version__ = version = '0.2.4'
22
+ __version_tuple__ = version_tuple = (0, 2, 4)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -14,6 +14,7 @@ from collections.abc import Callable
14
14
  from datetime import UTC, datetime
15
15
  from typing import TYPE_CHECKING, Any
16
16
 
17
+ from hyperweave import __version__
17
18
  from hyperweave.core.enums import ArtifactStatus, FrameType, MotionId
18
19
 
19
20
  if TYPE_CHECKING:
@@ -152,6 +153,8 @@ def _base_context(
152
153
  # Timestamp
153
154
  "created": datetime.now(UTC).isoformat(),
154
155
  "created_at": datetime.now(UTC).isoformat(),
156
+ # Version -- read by templates/components/metadata.svg.j2
157
+ "version": __version__,
155
158
  # Embedded fonts (base64 @font-face CSS)
156
159
  "font_faces": _load_font_faces(resolved.genome),
157
160
  }
@@ -11,6 +11,18 @@ def compose(spec: ComposeSpec) -> ComposeResult:
11
11
  """Compose an artifact from a ComposeSpec."""
12
12
  start = time.monotonic()
13
13
 
14
+ # ── 0. Infer state from value when the caller left the default ──
15
+ # Any explicit override (?state=failing, --state passing, MCP state arg,
16
+ # etc.) sets spec.state to something other than "active" and survives
17
+ # this step untouched. This is the single chokepoint covering HTTP,
18
+ # CLI, MCP, and kit.
19
+ if spec.state == "active" and spec.value:
20
+ from hyperweave.core.state import infer_state
21
+
22
+ inferred = infer_state(spec.title, spec.value)
23
+ if inferred != "active":
24
+ spec = spec.model_copy(update={"state": inferred})
25
+
14
26
  # ── 1. Resolve genome, profile, frame ──
15
27
  from hyperweave.compose.resolver import resolve
16
28
 
@@ -148,10 +148,17 @@ def resolve_badge(
148
148
  if use_mono and label_display:
149
149
  lw += len(label_display) * font_size * 0.06
150
150
 
151
- # System fonts (non-mono) are wider than the Inter LUT
152
- if not use_mono:
153
- lw *= 1.15
154
- vw *= 1.10
151
+ # Empirical width factors the Inter/mono LUTs approximate actual rendered
152
+ # width, but genomes can override per-zone to match their declared fonts.
153
+ # chrome-horizon, for example, renders badge values in Orbitron (~1.35x
154
+ # Inter). New genomes should measure their display font and declare their
155
+ # own `text_metrics` to avoid value-zone overflow. Defaults preserve the
156
+ # pre-v0.2.3 behavior so existing genomes are unaffected.
157
+ tm = genome.get("text_metrics", {})
158
+ default_label_factor = 1.15 if not use_mono else 1.0
159
+ default_value_factor = 1.10 if not use_mono else 1.0
160
+ lw *= tm.get("badge_label_width_factor", default_label_factor)
161
+ vw *= tm.get("badge_value_width_factor", default_value_factor)
155
162
 
156
163
  has_glyph = bool(spec.glyph or spec.custom_glyph_svg)
157
164
 
@@ -242,9 +249,8 @@ def resolve_strip(
242
249
  height = 52
243
250
 
244
251
  metrics = _parse_metrics(spec)
245
- metric_pitch = profile.get("strip_metric_pitch", 106)
252
+ min_metric_pitch = profile.get("strip_metric_pitch", 106)
246
253
  cell_offset = 12 # gap between first divider and first metric cell
247
- status_zone = 56 # 14px indicator + padding
248
254
 
249
255
  # Glyph zone is ~36px (12 pad + 24 glyph + gap). Collapse when absent.
250
256
  has_glyph = bool(glyph_data and glyph_data.get("path"))
@@ -260,6 +266,42 @@ def resolve_strip(
260
266
  # Account for letter-spacing 0.18em on identity text
261
267
  id_text_w += len(identity) * 11 * 0.18
262
268
  first_divider_x = max(int(identity_x + id_text_w + 14), 80) # 14px right padding, min 80
269
+
270
+ # ── Adaptive cell pitch (preserves the slot grid) ──
271
+ # Uniform pitch adopts the widest metric's measured width so the slot
272
+ # grid stays consistent (every cell is the same size), but no metric's
273
+ # value overflows its cell. This is where adaptivity lives: `for metric
274
+ # in metrics` varies by payload, and each metric's value width drives
275
+ # the shared pitch upward when values are long. The status diamond
276
+ # lives in the dedicated right-edge ``status_zone``, not per-cell.
277
+ cell_pad = 20 # horizontal breathing room inside each cell
278
+
279
+ # Typography params for width measurement. For chrome paradigm these
280
+ # should match the CSS declared in strip/chrome-defs.j2 (Orbitron 17px
281
+ # 900 for value, JetBrains Mono 9px 600 for label). Other paradigms
282
+ # fall back to profile-declared sizes.
283
+ paradigm = (genome.get("paradigms") or {}).get("strip", "default")
284
+ if paradigm == "chrome":
285
+ value_size = 17
286
+ label_size = 9
287
+ else:
288
+ value_size = profile.get("strip_metric_value_size", 18)
289
+ label_size = profile.get("strip_metric_label_size", 7)
290
+
291
+ widest_cell = min_metric_pitch
292
+ for metric in metrics:
293
+ raw_value = str(metric.get("value", ""))
294
+ raw_label = str(metric.get("label", "")).upper()
295
+ value_w = measure_text(raw_value, font_size=value_size, bold=True, monospace=False)
296
+ label_w = measure_text(raw_label, font_size=label_size, bold=False, monospace=True)
297
+ cell_content_w = max(label_w, value_w)
298
+ needed = int(cell_content_w + cell_pad)
299
+ if needed > widest_cell:
300
+ widest_cell = needed
301
+
302
+ metric_pitch = widest_cell
303
+ status_zone = 56 # reserved for right-edge status diamond + padding
304
+
263
305
  n = max(len(metrics), 1)
264
306
  width = first_divider_x + cell_offset + n * metric_pitch + status_zone
265
307
 
@@ -1622,7 +1664,7 @@ def _resolve_motion(spec: ComposeSpec, genome: dict[str, Any]) -> str:
1622
1664
  return MotionId.STATIC
1623
1665
 
1624
1666
 
1625
- def _parse_metrics(spec: ComposeSpec) -> list[dict[str, str]]:
1667
+ def _parse_metrics(spec: ComposeSpec) -> list[dict[str, Any]]:
1626
1668
  metrics: list[dict[str, str]] = []
1627
1669
 
1628
1670
  # Try slots first
@@ -0,0 +1,139 @@
1
+ """Chart frame resolver — star history / time-series visualization.
2
+
3
+ Reads pre-fetched connector data from ``spec.connector_data`` and delegates
4
+ the actual SVG math to :mod:`hyperweave.render.chart_engine`.
5
+
6
+ Three-state truthfulness contract:
7
+ - ``connector_data is None`` → ``data-hw-status="stale"``, "DATA UNAVAILABLE" overlay
8
+ - ``current_stars == 0`` (new repo) → ``data-hw-status="empty"``, "NEW REPO · NO STARS YET" overlay
9
+ - real points + current_stars > 0 → ``data-hw-status="fresh"``, live chart
10
+
11
+ The chart never fabricates data. There is no placeholder series — a zero-star
12
+ repo is a legitimate state, and upstream failure is rendered truthfully as
13
+ unavailable rather than masked with demo data.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from typing import TYPE_CHECKING, Any
19
+
20
+ from hyperweave.render.chart_engine import Viewport, build_chart_svg
21
+
22
+ if TYPE_CHECKING:
23
+ from hyperweave.core.models import ComposeSpec
24
+
25
+
26
+ # Default milestones for star charts (shown when values cross these thresholds).
27
+ _DEFAULT_MILESTONES: list[int] = [500, 1000, 2000, 5000, 10000]
28
+
29
+
30
+ def resolve_chart(
31
+ spec: ComposeSpec,
32
+ genome: dict[str, Any],
33
+ profile: dict[str, Any],
34
+ **_kw: Any,
35
+ ) -> dict[str, Any]:
36
+ """Resolve the ``chart`` frame into width/height/template/context."""
37
+ width, height = 900, 500
38
+
39
+ # Viewport insets: leave room for hero title at top, axis labels on left,
40
+ # date labels at bottom, and hero value at right.
41
+ # Dimensions match the target SVGs in tier2/genomes/.
42
+ paradigm = genome.get("paradigms", {}).get("chart", "brutalist")
43
+ vp = (
44
+ Viewport(x=80, y=160, w=750, h=250)
45
+ if paradigm == "chrome"
46
+ else Viewport(x=80, y=150, w=760, h=245)
47
+ )
48
+
49
+ # Three-state machine. "fresh" preserved (not renamed to "live") for
50
+ # backward compat with the existing data-hw-status contract; "empty" is
51
+ # new and specifically marks a truthful zero-star state.
52
+ connector = spec.connector_data
53
+ raw_points: list[Any]
54
+ empty_message: str | None
55
+ if connector is None:
56
+ # Upstream API failure — no data to trust.
57
+ status = "stale"
58
+ raw_points = []
59
+ current_stars = 0
60
+ empty_message = "DATA UNAVAILABLE"
61
+ else:
62
+ current_stars = int(
63
+ connector.get("current_stars") or connector.get("stars_total") or 0
64
+ )
65
+ raw_points = list(
66
+ connector.get("points") or connector.get("star_history") or []
67
+ )
68
+ if current_stars == 0:
69
+ # Truthful zero-star state (brand-new repo) — render empty, don't fabricate.
70
+ status = "empty"
71
+ raw_points = []
72
+ empty_message = "NEW REPO · NO STARS YET"
73
+ elif not raw_points:
74
+ # Has stars but no history — shouldn't happen after the connector
75
+ # fix, but degrade truthfully rather than synthesize.
76
+ status = "stale"
77
+ empty_message = "HISTORY UNAVAILABLE"
78
+ else:
79
+ status = "fresh"
80
+ empty_message = None
81
+
82
+ # Structural hints come from the resolver injection in compose/resolver.py,
83
+ # but we also read directly from the genome here because this file is
84
+ # imported before _resolve_paradigm has run (resolvers run INSIDE resolve()).
85
+ structural = genome.get("structural") or {}
86
+
87
+ chart_fragments = build_chart_svg(
88
+ raw_points,
89
+ vp,
90
+ structural,
91
+ milestones=_DEFAULT_MILESTONES,
92
+ empty_message=empty_message,
93
+ )
94
+
95
+ repo = connector.get("repo") if connector else None
96
+ repo = repo or f"{spec.chart_owner}/{spec.chart_repo}".strip("/")
97
+
98
+ # Hero identity strings shown at top + right of the standalone chart.
99
+ title_upper = (repo or "star history").upper()
100
+ current_display = _format_compact(int(current_stars))
101
+
102
+ ctx: dict[str, Any] = {
103
+ "chart_repo": repo,
104
+ "chart_title": title_upper,
105
+ "chart_current_stars": current_display,
106
+ "chart_viewport_x": vp.x,
107
+ "chart_viewport_y": vp.y,
108
+ "chart_viewport_w": vp.w,
109
+ "chart_viewport_h": vp.h,
110
+ "chart_defs": chart_fragments["defs"],
111
+ "chart_axes": chart_fragments["axes"],
112
+ "chart_gridlines": chart_fragments["gridlines"],
113
+ "chart_area": chart_fragments["area"],
114
+ "chart_polyline": chart_fragments["polyline"],
115
+ "chart_markers": chart_fragments["markers"],
116
+ "chart_milestones": chart_fragments["milestones"],
117
+ "chart_y_labels": chart_fragments["y_labels"],
118
+ "chart_x_labels": chart_fragments["x_labels"],
119
+ "chart_empty_state": chart_fragments["empty_state"],
120
+ "data_hw_status": status,
121
+ }
122
+ # Surface non-fresh states via the document-level data-hw-status attribute.
123
+ # "fresh" stays implicit (live data is the default, no status marker needed).
124
+ if status != "fresh":
125
+ ctx["status"] = status
126
+
127
+ return {
128
+ "width": width,
129
+ "height": height,
130
+ "template": "frames/chart.svg.j2",
131
+ "context": ctx,
132
+ }
133
+
134
+
135
+ def _format_compact(n: int) -> str:
136
+ """Render an integer as a compact string (2850 → '2,850', 12847 → '12.8K')."""
137
+ if n >= 10000:
138
+ return f"{n / 1000:.1f}K".rstrip("0").rstrip(".")
139
+ return f"{n:,}"
@@ -124,11 +124,18 @@ def resolve_stats(
124
124
  paradigm = genome.get("paradigms", {}).get("stats", "brutalist")
125
125
  if paradigm == "chrome":
126
126
  embed_vp = Viewport(x=240, y=170, w=220, h=70)
127
- chart_points = (
128
- connector.get("points")
129
- or connector.get("star_history")
130
- or _synthetic_series_from_total(int(stars_total or 1200))
131
- )
127
+ # Zero-guard: never default to a 1200-star synthetic curve. When
128
+ # stars_total is zero, the truthful state is an empty embedded chart.
129
+ stars_int = int(stars_total or 0)
130
+ real_points = connector.get("points") or connector.get("star_history")
131
+ if real_points:
132
+ chart_points: list[dict[str, Any]] = list(real_points)
133
+ elif stars_int > 0:
134
+ # Only synthesize when we know the total — this approximates a
135
+ # plausible growth curve rather than fabricating it from nothing.
136
+ chart_points = _synthetic_series_from_total(stars_int)
137
+ else:
138
+ chart_points = []
132
139
  embed = build_chart_svg(
133
140
  chart_points,
134
141
  embed_vp,
@@ -10,6 +10,8 @@ from urllib.parse import urlparse
10
10
 
11
11
  import httpx
12
12
 
13
+ from hyperweave import __version__
14
+
13
15
  # SSRF Protection
14
16
 
15
17
  ALLOWED_HOSTS: frozenset[str] = frozenset(
@@ -161,7 +163,7 @@ async def fetch(
161
163
  )
162
164
 
163
165
  merged_headers: dict[str, str] = {
164
- "User-Agent": "HyperWeave/0.1.0 (https://hyperweave.app)",
166
+ "User-Agent": f"HyperWeave/{__version__} (https://hyperweave.app)",
165
167
  "Accept": "application/json",
166
168
  }
167
169
  if headers:
@@ -185,6 +185,37 @@ async def fetch_stargazer_history(
185
185
 
186
186
  # Step 2: compute total pages
187
187
  total_pages = max(1, math.ceil(total_stars / _STARGAZER_PAGE_SIZE))
188
+
189
+ # Single-page case: repo has ≤ 30 stars. The "first starred_at of the page"
190
+ # sampling trick would otherwise collapse to a single aggregated point plus
191
+ # a duplicate-date "now" point, producing a zero time-range polyline. Use
192
+ # each stargazer's own timestamp instead — the whole page fits in one call.
193
+ if total_pages == 1:
194
+ single_page_url = (
195
+ f"https://api.github.com/repos/{identifier}/stargazers"
196
+ f"?per_page={_STARGAZER_PAGE_SIZE}&page=1"
197
+ )
198
+ page_payload = await fetch_json(
199
+ single_page_url,
200
+ provider=PROVIDER,
201
+ headers={"Accept": _STARGAZER_ACCEPT_HEADER},
202
+ )
203
+ single_page_points: list[dict[str, Any]] = []
204
+ if isinstance(page_payload, list):
205
+ for idx, entry in enumerate(page_payload):
206
+ if isinstance(entry, dict) and entry.get("starred_at"):
207
+ single_page_points.append(
208
+ {"date": entry["starred_at"], "count": idx + 1}
209
+ )
210
+ single_page_result: dict[str, Any] = {
211
+ "points": single_page_points,
212
+ "current_stars": total_stars,
213
+ "repo": identifier,
214
+ "ttl": STARGAZER_HISTORY_TTL,
215
+ }
216
+ cache.set(cache_key, single_page_result, STARGAZER_HISTORY_TTL)
217
+ return single_page_result
218
+
188
219
  # Cap the sample count at the number of available pages.
189
220
  sample_count = min(sample_pages, total_pages)
190
221
 
@@ -211,6 +211,19 @@ class GenomeSpec(BaseModel):
211
211
  description="Material hints: surface (matte/gloss), depth, filter_chain",
212
212
  )
213
213
 
214
+ # -- Text metrics (optional, per-zone width factors for empirical calibration) --
215
+ # The text-measurement LUT is Inter-calibrated; genomes that render with wider
216
+ # fonts (e.g. Orbitron 900 for chrome-horizon badge values) declare a
217
+ # post-measurement multiplier per zone. Defaults preserve pre-v0.2.3 behavior
218
+ # (1.0 for mono, 1.10-1.15 for non-mono via the resolver fallback).
219
+ # Known zone keys: badge_label_width_factor, badge_value_width_factor.
220
+ # Future zones (strip_value_width_factor, banner_hero_width_factor, etc.) can
221
+ # be added without schema change.
222
+ text_metrics: dict[str, float] = Field(
223
+ default_factory=dict,
224
+ description="Per-zone text width multipliers (e.g. badge_value_width_factor=1.35 for Orbitron)",
225
+ )
226
+
214
227
  # -- Kinetic cascade (optional, motion timing + compatible vocab) --
215
228
  motion_config: dict[str, Any] = Field(
216
229
  default_factory=dict,
@@ -0,0 +1,45 @@
1
+ """State inference from label + value strings.
2
+
3
+ Maps a (label, value) pair to one of the ArtifactStatus member values. Called
4
+ by compose() to auto-populate spec.state when the caller left it at the
5
+ default "active", so that a live badge whose fetched value is "failing"
6
+ renders as failing without every route having to remember to call this.
7
+
8
+ Inference is deliberately conservative: it returns "active" whenever no rule
9
+ fires, which compose() treats as "caller did not override, leave alone".
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+
15
+ def infer_state(label: str, value: str) -> str:
16
+ """Infer a semantic state from a (label, value) pair.
17
+
18
+ Returns one of: passing, failing, warning, building, critical, active.
19
+ "active" means "could not infer" — callers should treat it as unchanged.
20
+ """
21
+ label_lower = label.lower()
22
+ value_lower = value.lower()
23
+
24
+ if "pass" in value_lower or "success" in value_lower:
25
+ return "passing"
26
+ if "fail" in value_lower or "error" in value_lower:
27
+ return "failing"
28
+ if "warn" in value_lower:
29
+ return "warning"
30
+ if "build" in label_lower and "run" in value_lower:
31
+ return "building"
32
+
33
+ # Percentage-based threshold (e.g. "95%", "72.5%")
34
+ if value.rstrip("%").replace(".", "").isdigit():
35
+ try:
36
+ num = float(value.rstrip("%"))
37
+ if num >= 90:
38
+ return "passing"
39
+ if num >= 70:
40
+ return "warning"
41
+ return "critical"
42
+ except ValueError:
43
+ pass
44
+
45
+ return "active"
@@ -119,6 +119,10 @@
119
119
  "hero_weight": 900,
120
120
  "weight_hierarchy": "900/700/500/400"
121
121
  },
122
+ "text_metrics": {
123
+ "badge_value_width_factor": 1.35,
124
+ "badge_label_width_factor": 1.0
125
+ },
122
126
  "material": {
123
127
  "surface": "metallic",
124
128
  "depth": "deep",
@@ -16,7 +16,7 @@ badge_value_size: 11
16
16
  badge_value_weight: 700
17
17
  strip_corner: 0
18
18
  badge_corner: 0
19
- strip_accent_width: 6
19
+ strip_accent_width: 4
20
20
  strip_metric_pitch: 107
21
21
  strip_divider_mode: minimal
22
22
  badge_frame_height: 22
@@ -51,7 +51,7 @@ strip_metric_label_y: 17
51
51
  strip_metric_value_weight: 800
52
52
  strip_metric_value_fill: "var(--dna-ink-primary)"
53
53
  strip_metric_value_y: 38
54
- strip_metric_value_skew: -8
54
+ strip_metric_value_skew: 0
55
55
  strip_identity_font: "var(--dna-font-mono, 'Courier New', monospace)"
56
56
  strip_metric_label_font: "var(--dna-font-mono, 'Courier New', monospace)"
57
57
 
@@ -31,14 +31,14 @@ def _readme_kit(
31
31
  # Parse badges: "build:passing,version:v0.6.3,coverage:92%"
32
32
  badge_pairs = _parse_badge_string(badges)
33
33
 
34
- # Generate badges
34
+ # Generate badges -- compose() infers state from value at the chokepoint,
35
+ # so we leave it at the default and let that logic fire.
35
36
  for label, value in badge_pairs:
36
37
  spec = ComposeSpec(
37
38
  type="badge",
38
39
  genome_id=genome,
39
40
  title=label,
40
41
  value=value,
41
- state=_infer_state(label, value),
42
42
  )
43
43
  results[f"badge-{label.lower()}"] = compose(spec)
44
44
 
@@ -97,31 +97,3 @@ def _parse_badge_string(badges: str) -> list[tuple[str, str]]:
97
97
  k, v = pair.split(":", 1)
98
98
  pairs.append((k.strip(), v.strip()))
99
99
  return pairs
100
-
101
-
102
- def _infer_state(label: str, value: str) -> str:
103
- label_lower = label.lower()
104
- value_lower = value.lower()
105
-
106
- if "pass" in value_lower or "success" in value_lower:
107
- return "passing"
108
- if "fail" in value_lower or "error" in value_lower:
109
- return "failing"
110
- if "warn" in value_lower:
111
- return "warning"
112
- if "build" in label_lower and "run" in value_lower:
113
- return "building"
114
-
115
- # Percentage-based threshold
116
- if value.rstrip("%").replace(".", "").isdigit():
117
- try:
118
- num = float(value.rstrip("%"))
119
- if num >= 90:
120
- return "passing"
121
- if num >= 70:
122
- return "warning"
123
- return "critical"
124
- except ValueError:
125
- pass
126
-
127
- return "active"