hyperweave 0.2.24__tar.gz → 0.2.26__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 (395) hide show
  1. {hyperweave-0.2.24 → hyperweave-0.2.26}/CHANGELOG.md +28 -0
  2. {hyperweave-0.2.24 → hyperweave-0.2.26}/PKG-INFO +2 -1
  3. {hyperweave-0.2.24 → hyperweave-0.2.26}/README.md +1 -0
  4. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/_version.py +2 -2
  5. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/compose/context.py +5 -0
  6. hyperweave-0.2.26/src/hyperweave/compose/engine.py +167 -0
  7. hyperweave-0.2.26/src/hyperweave/compose/layout.py +303 -0
  8. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/compose/resolver.py +112 -89
  9. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/config/loader.py +19 -0
  10. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/core/paradigm.py +18 -0
  11. hyperweave-0.2.26/src/hyperweave/data/badge_modes.yaml +66 -0
  12. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/css/expression.css +23 -7
  13. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/paradigms/cellular.yaml +10 -0
  14. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/document.svg.j2 +2 -0
  15. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/badge/brutalist-content.j2 +11 -11
  16. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/badge/cellular-content.j2 +8 -12
  17. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/badge/chrome-content.j2 +5 -5
  18. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/strip.svg.j2 +5 -0
  19. hyperweave-0.2.26/tests/snapshots/url_stability/divider_band_chrome_static.svg +195 -0
  20. hyperweave-0.2.26/tests/snapshots/url_stability/icon_github_chrome_static.svg +269 -0
  21. hyperweave-0.2.26/tests/test_badge_layout.py +281 -0
  22. hyperweave-0.2.26/tests/test_badge_mode.py +264 -0
  23. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_state_inference.py +51 -0
  24. hyperweave-0.2.26/tests/test_url_stability.py +123 -0
  25. hyperweave-0.2.24/src/hyperweave/compose/engine.py +0 -93
  26. {hyperweave-0.2.24 → hyperweave-0.2.26}/.dockerignore +0 -0
  27. {hyperweave-0.2.24 → hyperweave-0.2.26}/.github/workflows/ci.yml +0 -0
  28. {hyperweave-0.2.24 → hyperweave-0.2.26}/.github/workflows/deploy.yml +0 -0
  29. {hyperweave-0.2.24 → hyperweave-0.2.26}/.github/workflows/publish.yml +0 -0
  30. {hyperweave-0.2.24 → hyperweave-0.2.26}/.gitignore +0 -0
  31. {hyperweave-0.2.24 → hyperweave-0.2.26}/.pre-commit-config.yaml +0 -0
  32. {hyperweave-0.2.24 → hyperweave-0.2.26}/Dockerfile +0 -0
  33. {hyperweave-0.2.24 → hyperweave-0.2.26}/LICENSE +0 -0
  34. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/buttons/button-liquid.svg +0 -0
  35. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/cards/card-butterfly.svg +0 -0
  36. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/cards/card-python.svg +0 -0
  37. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/cards/card-sunflower.svg +0 -0
  38. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/cards/card-waves.svg +0 -0
  39. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/brutalist-emerald/badge_critical.svg +0 -0
  40. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/brutalist-emerald/badge_passing.svg +0 -0
  41. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/brutalist-emerald/badge_warning.svg +0 -0
  42. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/brutalist-emerald/banner.svg +0 -0
  43. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/brutalist-emerald/icons/discord.svg +0 -0
  44. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/brutalist-emerald/icons/notion.svg +0 -0
  45. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/brutalist-emerald/icons/reddit.svg +0 -0
  46. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/brutalist-emerald/icons/spotify.svg +0 -0
  47. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/brutalist-emerald/icons/youtube.svg +0 -0
  48. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/brutalist-emerald/marquee_counter.svg +0 -0
  49. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/brutalist-emerald/marquee_horizontal.svg +0 -0
  50. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/brutalist-emerald/marquee_vertical.svg +0 -0
  51. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/brutalist-emerald/profile-cards/chart_stars_full.svg +0 -0
  52. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/brutalist-emerald/profile-cards/stats.svg +0 -0
  53. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/brutalist-emerald/strip.svg +0 -0
  54. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/chrome-horizon/badge_critical.svg +0 -0
  55. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/chrome-horizon/badge_passing.svg +0 -0
  56. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/chrome-horizon/badge_warning.svg +0 -0
  57. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/chrome-horizon/banner.svg +0 -0
  58. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/chrome-horizon/icons/bluesky.svg +0 -0
  59. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/chrome-horizon/icons/github.svg +0 -0
  60. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/chrome-horizon/icons/instagram.svg +0 -0
  61. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/chrome-horizon/icons/mastodon.svg +0 -0
  62. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/chrome-horizon/icons/x.svg +0 -0
  63. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/chrome-horizon/marquee_counter.svg +0 -0
  64. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/chrome-horizon/marquee_horizontal.svg +0 -0
  65. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/chrome-horizon/marquee_vertical.svg +0 -0
  66. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/chrome-horizon/profile-cards/chart_stars_full.svg +0 -0
  67. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/chrome-horizon/profile-cards/stats.svg +0 -0
  68. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/chrome-horizon/strip.svg +0 -0
  69. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/telemetry/master_card.svg +0 -0
  70. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/telemetry/receipt.svg +0 -0
  71. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/telemetry/receipt_claude-code_medium.svg +0 -0
  72. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/telemetry/receipt_codex_large.svg +0 -0
  73. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/telemetry/receipt_voltage_xlarge.svg +0 -0
  74. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/telemetry/rhythm_strip.svg +0 -0
  75. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/examples/telemetry/rhythm_strip_voltage_xlarge.svg +0 -0
  76. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/footers/inneraura-footer-liquid.svg +0 -0
  77. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/footers/inneraura-footer-purple.svg +0 -0
  78. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/footers/inneraura-footer.svg +0 -0
  79. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/hyperweave-banner.svg +0 -0
  80. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/icons/cobalt-sapphire-discord.svg +0 -0
  81. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/icons/cobalt-sapphire-docs.svg +0 -0
  82. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/icons/cobalt-sapphire-github.svg +0 -0
  83. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/icons/cobalt-sapphire-instagram.svg +0 -0
  84. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/icons/cobalt-sapphire-linkedin.svg +0 -0
  85. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/icons/cobalt-sapphire-tiktok.svg +0 -0
  86. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/icons/cobalt-sapphire-x.svg +0 -0
  87. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/icons/cobalt-sapphire-youtube.svg +0 -0
  88. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/marquees/badge-showcase-triple.svg +0 -0
  89. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/marquees/genome-marquee-triple.svg +0 -0
  90. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/marquees/sample-badges.svg +0 -0
  91. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/mintlify-assets/404.svg +0 -0
  92. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/mintlify-assets/callout-icons.svg +0 -0
  93. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/mintlify-assets/divider.svg +0 -0
  94. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/mintlify-assets/favicon.svg +0 -0
  95. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/mintlify-assets/hero-banner.svg +0 -0
  96. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/mintlify-assets/hyperweave-banner-v2.svg +0 -0
  97. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/mintlify-assets/hyperweave-navbar-logo.svg +0 -0
  98. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/mintlify-assets/loader.svg +0 -0
  99. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/mintlify-assets/og-image.svg +0 -0
  100. {hyperweave-0.2.24 → hyperweave-0.2.26}/assets/timelines/hyperweave-roadmap.svg +0 -0
  101. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/assets/favicon.svg +0 -0
  102. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/assets/hyperweave-banner.svg +0 -0
  103. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/assets/hyperweave-navbar-logo.svg +0 -0
  104. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/assets/og-image.svg +0 -0
  105. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/explanation/architecture.mdx +0 -0
  106. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/explanation/camo-compatibility.mdx +0 -0
  107. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/explanation/cim-compliance.mdx +0 -0
  108. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/explanation/genome-profile-system.mdx +0 -0
  109. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/explanation/metadata-tiers.mdx +0 -0
  110. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/how-to/add-motion-to-badges.mdx +0 -0
  111. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/how-to/create-session-receipts.mdx +0 -0
  112. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/how-to/set-up-claude-code-hooks.mdx +0 -0
  113. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/how-to/use-live-data-badges.mdx +0 -0
  114. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/introduction.mdx +0 -0
  115. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/mint.json +0 -0
  116. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/quickstart.mdx +0 -0
  117. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/reference/cli.mdx +0 -0
  118. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/reference/compose-spec.mdx +0 -0
  119. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/reference/genomes.mdx +0 -0
  120. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/reference/http-api.mdx +0 -0
  121. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/reference/mcp-tools.mdx +0 -0
  122. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/reference/motions.mdx +0 -0
  123. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/reference/telemetry-contract.mdx +0 -0
  124. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/spec/hyperweave-protocol.mdx +0 -0
  125. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/tutorials/build-an-artifact-kit.mdx +0 -0
  126. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/tutorials/compose-your-first-badge.mdx +0 -0
  127. {hyperweave-0.2.24 → hyperweave-0.2.26}/docs/tutorials/install-session-telemetry.mdx +0 -0
  128. {hyperweave-0.2.24 → hyperweave-0.2.26}/fly.toml +0 -0
  129. {hyperweave-0.2.24 → hyperweave-0.2.26}/hooks/install.py +0 -0
  130. {hyperweave-0.2.24 → hyperweave-0.2.26}/hooks/session_end.sh +0 -0
  131. {hyperweave-0.2.24 → hyperweave-0.2.26}/justfile +0 -0
  132. {hyperweave-0.2.24 → hyperweave-0.2.26}/pyproject.toml +0 -0
  133. {hyperweave-0.2.24 → hyperweave-0.2.26}/scripts/extract_font_metrics.py +0 -0
  134. {hyperweave-0.2.24 → hyperweave-0.2.26}/scripts/extract_glyphs.py +0 -0
  135. {hyperweave-0.2.24 → hyperweave-0.2.26}/scripts/generate_proofset.py +0 -0
  136. {hyperweave-0.2.24 → hyperweave-0.2.26}/scripts/probe_live_stats.py +0 -0
  137. {hyperweave-0.2.24 → hyperweave-0.2.26}/scripts/probe_star_history.py +0 -0
  138. {hyperweave-0.2.24 → hyperweave-0.2.26}/scripts/stress_test.py +0 -0
  139. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/__init__.py +0 -0
  140. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/__main__.py +0 -0
  141. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/cli.py +0 -0
  142. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/compose/__init__.py +0 -0
  143. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/compose/assembler.py +0 -0
  144. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/compose/bar_chart.py +0 -0
  145. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/compose/lanes.py +0 -0
  146. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/compose/resolvers/__init__.py +0 -0
  147. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/compose/resolvers/chart.py +0 -0
  148. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/compose/resolvers/stats.py +0 -0
  149. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/compose/rhythm.py +0 -0
  150. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/compose/rhythm_strip.py +0 -0
  151. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/compose/treemap.py +0 -0
  152. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/compose/validate_paradigms.py +0 -0
  153. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/config/__init__.py +0 -0
  154. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/config/genome_validator.py +0 -0
  155. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/config/registry.py +0 -0
  156. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/config/settings.py +0 -0
  157. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/connectors/__init__.py +0 -0
  158. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/connectors/arxiv.py +0 -0
  159. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/connectors/base.py +0 -0
  160. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/connectors/cache.py +0 -0
  161. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/connectors/github.py +0 -0
  162. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/connectors/rest.py +0 -0
  163. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/core/__init__.py +0 -0
  164. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/core/cell_layout.py +0 -0
  165. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/core/color.py +0 -0
  166. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/core/contracts.py +0 -0
  167. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/core/enums.py +0 -0
  168. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/core/font_metrics.py +0 -0
  169. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/core/models.py +0 -0
  170. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/core/schema.py +0 -0
  171. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/core/state.py +0 -0
  172. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/core/text.py +0 -0
  173. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/core/thresholds.py +0 -0
  174. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/css/accessibility.css +0 -0
  175. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/css/bridge.css +0 -0
  176. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/css/status.css +0 -0
  177. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/font-metrics/chakra-petch.json +0 -0
  178. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/font-metrics/inter.json +0 -0
  179. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/font-metrics/jetbrains-mono.json +0 -0
  180. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/font-metrics/orbitron.json +0 -0
  181. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/fonts/chakra-petch.b64 +0 -0
  182. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/fonts/chakra-petch.meta.json +0 -0
  183. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/fonts/jetbrains-mono.b64 +0 -0
  184. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/fonts/jetbrains-mono.meta.json +0 -0
  185. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/fonts/orbitron.b64 +0 -0
  186. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/fonts/orbitron.meta.json +0 -0
  187. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/genomes/automata.json +0 -0
  188. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/genomes/brutalist.json +0 -0
  189. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/genomes/chrome.json +0 -0
  190. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/genomes/telemetry-claude-code.json +0 -0
  191. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/genomes/telemetry-codex.json +0 -0
  192. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/genomes/telemetry-cream.json +0 -0
  193. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/genomes/telemetry-voltage.json +0 -0
  194. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/glyphs.json +0 -0
  195. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/motions/border/chromatic-pulse.yaml +0 -0
  196. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/motions/border/corner-trace.yaml +0 -0
  197. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/motions/border/dual-orbit.yaml +0 -0
  198. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/motions/border/entanglement.yaml +0 -0
  199. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/motions/border/rimrun.yaml +0 -0
  200. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/motions/static.yaml +0 -0
  201. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/paradigms/brutalist.yaml +0 -0
  202. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/paradigms/chrome.yaml +0 -0
  203. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/paradigms/default.yaml +0 -0
  204. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/policies/normal.json +0 -0
  205. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/policies/permissive.json +0 -0
  206. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/policies/ungoverned.json +0 -0
  207. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/profiles/brutalist.contract.json +0 -0
  208. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/profiles/brutalist.yaml +0 -0
  209. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/profiles/chrome.contract.json +0 -0
  210. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/profiles/chrome.yaml +0 -0
  211. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/specimens.yaml +0 -0
  212. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/telemetry/model-pricing.yaml +0 -0
  213. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/telemetry/runtimes/claude-code.yaml +0 -0
  214. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/telemetry/runtimes/codex.yaml +0 -0
  215. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/telemetry/stage-config.yaml +0 -0
  216. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/telemetry/stage-labels.yaml +0 -0
  217. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/telemetry/tool-colors.yaml +0 -0
  218. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/terminals/arrow.json +0 -0
  219. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/terminals/aurora.json +0 -0
  220. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/terminals/beacon.json +0 -0
  221. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/terminals/bracket3d.json +0 -0
  222. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/terminals/chevron.json +0 -0
  223. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/terminals/crosshair.json +0 -0
  224. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/terminals/diamond.json +0 -0
  225. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/terminals/rocket.json +0 -0
  226. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/terminals/rules/bar.json +0 -0
  227. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/terminals/rules/dashed.json +0 -0
  228. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/terminals/rules/five-wave.json +0 -0
  229. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/terminals/rules/spectral.json +0 -0
  230. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/terminals/rules/straight.json +0 -0
  231. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/terminals/rules/wave.json +0 -0
  232. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/data/terminals/stijl.json +0 -0
  233. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/kit.py +0 -0
  234. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/mcp/__init__.py +0 -0
  235. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/mcp/__main__.py +0 -0
  236. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/mcp/server.py +0 -0
  237. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/py.typed +0 -0
  238. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/render/__init__.py +0 -0
  239. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/render/chart_engine.py +0 -0
  240. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/render/fonts.py +0 -0
  241. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/render/glyphs.py +0 -0
  242. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/render/motion.py +0 -0
  243. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/render/templates.py +0 -0
  244. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/serve/__init__.py +0 -0
  245. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/serve/app.py +0 -0
  246. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/serve/data_tokens.py +0 -0
  247. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/telemetry/__init__.py +0 -0
  248. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/telemetry/capture.py +0 -0
  249. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/telemetry/codex_parser.py +0 -0
  250. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/telemetry/contract.py +0 -0
  251. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/telemetry/corrections.py +0 -0
  252. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/telemetry/cost.py +0 -0
  253. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/telemetry/models.py +0 -0
  254. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/telemetry/parser.py +0 -0
  255. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/telemetry/runtimes.py +0 -0
  256. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/telemetry/stages.py +0 -0
  257. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/components/chart-area.svg.j2 +0 -0
  258. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/components/chart-axes.svg.j2 +0 -0
  259. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/components/chart-empty-state.svg.j2 +0 -0
  260. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/components/chart-gridlines.svg.j2 +0 -0
  261. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/components/chart-markers/circle.svg.j2 +0 -0
  262. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/components/chart-markers/diamond.svg.j2 +0 -0
  263. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/components/chart-markers/endpoint-diamond.svg.j2 +0 -0
  264. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/components/chart-markers/endpoint-rect.svg.j2 +0 -0
  265. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/components/chart-markers/rect.svg.j2 +0 -0
  266. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/components/chart-milestone.svg.j2 +0 -0
  267. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/components/chart-polyline.svg.j2 +0 -0
  268. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/components/filter.svg.j2 +0 -0
  269. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/components/glyph-inline.svg.j2 +0 -0
  270. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/components/glyph.svg.j2 +0 -0
  271. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/components/gradient.svg.j2 +0 -0
  272. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/components/metadata.svg.j2 +0 -0
  273. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/components/metric.svg.j2 +0 -0
  274. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/components/phase-segment.svg.j2 +0 -0
  275. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/components/rule.svg.j2 +0 -0
  276. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/components/status.svg.j2 +0 -0
  277. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/components/terminal.svg.j2 +0 -0
  278. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/error-badge.svg.j2 +0 -0
  279. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/badge/brutalist-defs.j2 +0 -0
  280. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/badge/cellular-defs.j2 +0 -0
  281. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/badge/chrome-defs.j2 +0 -0
  282. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/badge/default-content.j2 +0 -0
  283. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/badge/default-defs.j2 +0 -0
  284. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/badge.svg.j2 +0 -0
  285. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/catalog.svg.j2 +0 -0
  286. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/chart/brutalist-content.j2 +0 -0
  287. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/chart/brutalist-defs.j2 +0 -0
  288. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/chart/cellular-content.j2 +0 -0
  289. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/chart/cellular-defs.j2 +0 -0
  290. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/chart/chrome-content.j2 +0 -0
  291. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/chart/chrome-defs.j2 +0 -0
  292. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/chart.svg.j2 +0 -0
  293. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/divider/brutalist-seam.svg.j2 +0 -0
  294. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/divider/chrome-band.svg.j2 +0 -0
  295. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/divider.svg.j2 +0 -0
  296. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/icon/brutalist-content.j2 +0 -0
  297. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/icon/brutalist-defs.j2 +0 -0
  298. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/icon/cellular-content.j2 +0 -0
  299. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/icon/cellular-defs.j2 +0 -0
  300. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/icon/chrome-content.j2 +0 -0
  301. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/icon/chrome-defs.j2 +0 -0
  302. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/icon/default-content.j2 +0 -0
  303. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/icon/default-defs.j2 +0 -0
  304. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/icon.svg.j2 +0 -0
  305. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/marquee-horizontal/brutalist-content.j2 +0 -0
  306. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/marquee-horizontal/brutalist-defs.j2 +0 -0
  307. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/marquee-horizontal/brutalist-overlay.j2 +0 -0
  308. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/marquee-horizontal/cellular-content.j2 +0 -0
  309. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/marquee-horizontal/cellular-defs.j2 +0 -0
  310. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/marquee-horizontal/cellular-overlay.j2 +0 -0
  311. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/marquee-horizontal/chrome-content.j2 +0 -0
  312. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/marquee-horizontal/chrome-defs.j2 +0 -0
  313. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/marquee-horizontal/chrome-overlay.j2 +0 -0
  314. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/marquee-horizontal/default-content.j2 +0 -0
  315. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/marquee-horizontal/default-defs.j2 +0 -0
  316. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/marquee-horizontal.svg.j2 +0 -0
  317. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/master-card.svg.j2 +0 -0
  318. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/receipt.svg.j2 +0 -0
  319. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/rhythm-strip.svg.j2 +0 -0
  320. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/stats/brutalist-content.j2 +0 -0
  321. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/stats/brutalist-defs.j2 +0 -0
  322. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/stats/cellular-content.j2 +0 -0
  323. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/stats/cellular-defs.j2 +0 -0
  324. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/stats/chrome-content.j2 +0 -0
  325. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/stats/chrome-defs.j2 +0 -0
  326. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/stats.svg.j2 +0 -0
  327. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/strip/brutalist-content.j2 +0 -0
  328. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/strip/brutalist-defs.j2 +0 -0
  329. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/strip/brutalist-status.j2 +0 -0
  330. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/strip/cellular-content.j2 +0 -0
  331. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/strip/cellular-defs.j2 +0 -0
  332. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/strip/cellular-status.j2 +0 -0
  333. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/strip/chrome-content.j2 +0 -0
  334. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/strip/chrome-defs.j2 +0 -0
  335. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/strip/chrome-status.j2 +0 -0
  336. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/strip/default-content.j2 +0 -0
  337. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/strip/default-defs.j2 +0 -0
  338. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/frames/strip/default-status.j2 +0 -0
  339. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/motions/border/chromatic-pulse.svg.j2 +0 -0
  340. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/motions/border/corner-trace.svg.j2 +0 -0
  341. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/motions/border/dual-orbit.svg.j2 +0 -0
  342. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/motions/border/entanglement.svg.j2 +0 -0
  343. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/motions/border/rimrun.svg.j2 +0 -0
  344. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/partials/provider-glyphs.svg.j2 +0 -0
  345. {hyperweave-0.2.24 → hyperweave-0.2.26}/src/hyperweave/templates/partials/state-signal-cascade.j2 +0 -0
  346. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/__init__.py +0 -0
  347. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/conftest.py +0 -0
  348. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/fixtures/codex_session.jsonl +0 -0
  349. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/fixtures/codex_session_patches.jsonl +0 -0
  350. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/fixtures/github_contributions/synthetic.html +0 -0
  351. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/fixtures/session.json +0 -0
  352. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/fixtures/session.jsonl +0 -0
  353. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/helpers.py +0 -0
  354. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_assembler.py +0 -0
  355. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_badge_cellular.py +0 -0
  356. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_chart_engine.py +0 -0
  357. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_chart_frame.py +0 -0
  358. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_codex_parser.py +0 -0
  359. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_compose_bar_chart.py +0 -0
  360. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_compose_rhythm_strip.py +0 -0
  361. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_compose_treemap.py +0 -0
  362. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_compose_variant_field.py +0 -0
  363. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_connectors.py +0 -0
  364. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_core.py +0 -0
  365. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_data_tokens.py +0 -0
  366. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_divider_cellular.py +0 -0
  367. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_divider_genome_specific.py +0 -0
  368. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_font_metrics_chakra.py +0 -0
  369. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_genome_automata.py +0 -0
  370. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_github_scrape.py +0 -0
  371. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_kit.py +0 -0
  372. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_marquee_v0_2_16.py +0 -0
  373. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_mcp.py +0 -0
  374. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_paradigm_cellular.py +0 -0
  375. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_paradigm_dispatch.py +0 -0
  376. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_paradigm_extensibility.py +0 -0
  377. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_parse_transcript_auto.py +0 -0
  378. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_proofset.py +0 -0
  379. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_receipt_token_compliance.py +0 -0
  380. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_render.py +0 -0
  381. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_resolver_rhythm.py +0 -0
  382. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_runtime_registries.py +0 -0
  383. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_serve.py +0 -0
  384. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_serve_live_state.py +0 -0
  385. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_slot_metric_state.py +0 -0
  386. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_stats_card.py +0 -0
  387. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_strip_cellular.py +0 -0
  388. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_strip_status_toggle.py +0 -0
  389. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_telemetry.py +0 -0
  390. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_telemetry_contract_per_stage.py +0 -0
  391. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_telemetry_integration.py +0 -0
  392. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_telemetry_skin_dispatch.py +0 -0
  393. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_tool_class_completeness.py +0 -0
  394. {hyperweave-0.2.24 → hyperweave-0.2.26}/tests/test_variant_default_resolution.py +0 -0
  395. {hyperweave-0.2.24 → hyperweave-0.2.26}/uv.lock +0 -0
@@ -5,6 +5,34 @@ 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.26] - 2026-05-07
9
+
10
+ Two follow-ups to v0.2.25's badge state architecture: strip status indicators now color correctly when CI metrics fail, and the snapshot test suite no longer fails on local-vs-CI version differences.
11
+
12
+ ### Fixed
13
+
14
+ - **Strip status indicator color tracks the most severe CI metric** — a strip with `BUILD:failing` next to `STARS` and `VERSION` now renders the right-edge diamond red, matching pre-v0.2.25 behavior. The v0.2.25 narrowing inadvertently dropped this for strips because their title is the repo identifier (`HYPERWEAVE`, `readme-ai`), not a state-bearing label. The engine now parses strip metric cells, runs state inference per allowlisted cell (`BUILD`, `COVERAGE`, `LINT`, etc.), and rolls up to the most severe state across them. Pure data strips (no allowlisted cells) stay neutral.
15
+ - **Snapshot tests handle local-vs-CI version differences** — the URL stability suite was treating package version strings as content (`version="0.2.20"` vs `version="0.2.25"` from dynamic git-tag versioning), failing CI when local snapshots were captured at a different version than the runner. Versions in metadata are now normalized for comparison alongside UUIDs and timestamps.
16
+
17
+ ### Notes
18
+
19
+ - 960 tests (was 955). 5 new tests pin the strip rollup contract across failing / passing / mixed-severity / pure-data / explicit-override cases.
20
+
21
+ ## [0.2.25] - 2026-05-07
22
+
23
+ Production badges on data-driven URLs (`STARS`, `FORKS`, `VERSION`, `LICENSE`, `PYTHON`) no longer trigger false-alarm status indicators on numeric values. Badge value text now centers correctly for every value length across all genomes.
24
+
25
+ ### Fixed
26
+
27
+ - **Data badges no longer show false status indicators** — a `STARS` badge with value `6` rendered an orange warning diamond because the leading digit matched a threshold rule designed for percentages. Indicators now only appear on common build / test / CI / quality / deployment / monitoring titles (`BUILD`, `TESTS`, `CI`, `PIPELINE`, `WORKFLOW`, `COVERAGE`, `LINT`, `DEPLOY`, `RELEASE`, `STATUS`, `HEALTH`, `UPTIME`, etc.) or when explicitly set via `?state=`. Hyphen and underscore variants like `BUILD-STATUS` and `CI_CD` match the same canonical title without separate config entries.
28
+ - **Badge value text centers correctly across all genomes** — values drifted 1–3px right of true center, with automata badges drifting further than chrome and brutalist. Centering now verified within 0.5px across short percentages, version strings, license slugs, python_requires, and long names.
29
+ - **Monospace labels had extra padding on the right** — width measurement double-counted letter-spacing for mono labels like `BUILD` and `COVERAGE`; labels now match their actual rendered width.
30
+ - **Data-only strips drop the status diamond** — `STARS | FORKS | VERSION` strips render as pure data; mixed strips like `BUILD | STARS` keep the right-edge indicator; `?state=` always overrides.
31
+
32
+ ### Notes
33
+
34
+ - 955 tests (was 914). Divider and icon URLs render unchanged; existing badge URLs continue to work, with corrected centering and tighter widths where false indicators were removed.
35
+
8
36
  ## [0.2.24] - 2026-05-07
9
37
 
10
38
  Receipts no longer clip past the right edge of the card when one tool dominates the session. Cleanup pass removes an unused template, an unused styling table, and two silent default-position fallbacks in the receipt template.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperweave
3
- Version: 0.2.24
3
+ Version: 0.2.26
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
@@ -51,6 +51,7 @@ Description-Content-Type: text/markdown
51
51
  <img src="https://hyperweave.app/v1/badge/PYTHON/chrome.static?data=pypi:hyperweave.python_requires" alt="python"/>
52
52
  </p>
53
53
  -->
54
+
54
55
  <p align="center">
55
56
  <img src="https://hyperweave.app/v1/strip/hyperweave/chrome.static?data=gh:InnerAura/hyperweave.build,pypi:hyperweave.version,gh:InnerAura/hyperweave.license&glyph=github" alt="strip"/>
56
57
  </p>
@@ -19,6 +19,7 @@
19
19
  <img src="https://hyperweave.app/v1/badge/PYTHON/chrome.static?data=pypi:hyperweave.python_requires" alt="python"/>
20
20
  </p>
21
21
  -->
22
+
22
23
  <p align="center">
23
24
  <img src="https://hyperweave.app/v1/strip/hyperweave/chrome.static?data=gh:InnerAura/hyperweave.build,pypi:hyperweave.version,gh:InnerAura/hyperweave.license&glyph=github" alt="strip"/>
24
25
  </p>
@@ -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.24'
22
- __version_tuple__ = version_tuple = (0, 2, 24)
21
+ __version__ = version = '0.2.26'
22
+ __version_tuple__ = version_tuple = (0, 2, 26)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -117,6 +117,11 @@ def _base_context(
117
117
  "slots": [s.model_dump() for s in spec.slots],
118
118
  "numeric_value": spec.numeric_value,
119
119
  "data_hw_value": spec.numeric_value,
120
+ # v0.2.25: SVG-root attribute that gates threshold-CSS auto-tinting.
121
+ # Default "off" = stateless; resolvers override via frame_context.
122
+ # Values: "off" (stateless), "auto" (stateful, threshold-CSS may fire),
123
+ # "explicit" (?state= set; user owns the state, no auto-inference).
124
+ "data_hw_statemode": "off",
120
125
  # Profile
121
126
  "profile": profile,
122
127
  "badge_corner": profile.get("badge_corner", 3.33),
@@ -0,0 +1,167 @@
1
+ """Composition engine -- the single entry point for all artifact generation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import time
6
+
7
+ from hyperweave.core.models import ArtifactMetadata, ComposeResult, ComposeSpec, ResolvedArtifact
8
+
9
+
10
+ def compose(spec: ComposeSpec) -> ComposeResult:
11
+ """Compose an artifact from a ComposeSpec."""
12
+ start = time.monotonic()
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
+ #
20
+ # v0.2.25: gated on the stateful-title allowlist (data/badge_modes.yaml).
21
+ # Pre-v0.2.25 the inference fired on EVERY frame regardless of title —
22
+ # so a STARS=42 badge was auto-inferred to state="critical" (leading
23
+ # digit 4) and rendered an orange/red status indicator on a value with
24
+ # no semantic state.
25
+ #
26
+ # Frame-aware logic:
27
+ # Badge → title-keyed inference. Title in allowlist → run infer_state.
28
+ # Strip → per-cell rolled-up inference. Strip's spec.title is the
29
+ # repo identifier (HYPERWEAVE, readme-ai), not a state-
30
+ # bearing label — state lives in the metric cells. Parse
31
+ # cells, run infer_state on each allowlisted (label, value)
32
+ # pair, take the most severe state.
33
+ if spec.state == "active" and spec.value:
34
+ from hyperweave.compose.layout import normalize_title
35
+ from hyperweave.config.loader import load_badge_modes
36
+ from hyperweave.core.enums import FrameType
37
+ from hyperweave.core.state import infer_state
38
+
39
+ allowlist = load_badge_modes()
40
+ # Severity ordering when rolling up across multiple metric cells.
41
+ # critical / failing share the most-severe slot — both render red.
42
+ _SEVERITY = {
43
+ "active": 0,
44
+ "passing": 1,
45
+ "building": 2,
46
+ "warning": 3,
47
+ "critical": 4,
48
+ "failing": 4,
49
+ }
50
+
51
+ inferred: str = "active"
52
+ if spec.type == FrameType.BADGE and spec.title:
53
+ if normalize_title(spec.title) in allowlist:
54
+ inferred = infer_state(spec.title, spec.value)
55
+ elif spec.type == FrameType.STRIP:
56
+ # Roll up across metric cells. v0.2.24 and earlier accidentally
57
+ # got this for free because infer_state ran on the strip's
58
+ # whole comma-separated value string and found "fail" in
59
+ # "BUILD:failing" — a happy substring-match accident. v0.2.25's
60
+ # title gate broke that path. Per-cell inference is the
61
+ # principled replacement: only allowlisted cells contribute
62
+ # state, and we pick the most severe.
63
+ best_severity = 0
64
+ for label, value in _parse_strip_metric_pairs(spec):
65
+ if normalize_title(label) in allowlist:
66
+ cell_state = infer_state(label, value)
67
+ sev = _SEVERITY.get(cell_state, 0)
68
+ if sev > best_severity:
69
+ inferred = cell_state
70
+ best_severity = sev
71
+
72
+ if inferred != "active":
73
+ spec = spec.model_copy(update={"state": inferred})
74
+
75
+ # ── 1. Resolve genome, profile, frame ──
76
+ from hyperweave.compose.resolver import resolve
77
+
78
+ resolved = resolve(spec)
79
+
80
+ # ── 2. Assemble CSS ──
81
+ from hyperweave.compose.assembler import assemble_css
82
+
83
+ css_bundle = assemble_css(resolved, frame_type=spec.type)
84
+
85
+ # ── 3. Build template context ──
86
+ from hyperweave.compose.context import build_context
87
+
88
+ context = build_context(spec, resolved, css_bundle)
89
+
90
+ # ── 4. Enforce policy lanes ──
91
+ from hyperweave.compose.lanes import enforce
92
+
93
+ context = enforce(context, spec.regime)
94
+
95
+ # ── 5. Render template → SVG ──
96
+ from hyperweave.render.templates import render_artifact
97
+
98
+ svg = render_artifact(resolved.frame_template, context)
99
+
100
+ # ── 6. Build result ──
101
+ duration_ms = (time.monotonic() - start) * 1000
102
+
103
+ metadata = _build_metadata(spec, resolved, duration_ms)
104
+
105
+ result = ComposeResult(
106
+ svg=svg,
107
+ metadata=metadata,
108
+ width=resolved.width,
109
+ height=resolved.height,
110
+ )
111
+
112
+ # ── 7. Emit telemetry event (fire-and-forget) ──
113
+ try:
114
+ from hyperweave.telemetry.capture import emit_generation_event
115
+
116
+ emit_generation_event(spec, result)
117
+ except Exception:
118
+ pass # telemetry must never break compose
119
+
120
+ return result
121
+
122
+
123
+ def _parse_strip_metric_pairs(spec: ComposeSpec) -> list[tuple[str, str]]:
124
+ """Parse strip metric (label, value) pairs from spec.
125
+
126
+ Mirrors ``compose/resolver.py:_parse_metrics`` but lives here so engine
127
+ step 0 can run state inference before resolve() is called. Two sources:
128
+
129
+ * ``spec.slots`` entries with zones starting with ``metric`` —
130
+ ``slot.value`` of the form ``"LABEL:value"``.
131
+ * Comma-separated fallback in ``spec.value`` —
132
+ ``"STARS:2.9k,FORKS:278,BUILD:failing"``.
133
+ """
134
+ pairs: list[tuple[str, str]] = []
135
+ for slot in spec.slots:
136
+ if slot.zone.startswith("metric") and ":" in slot.value:
137
+ k, v = slot.value.split(":", 1)
138
+ pairs.append((k.strip(), v.strip()))
139
+ if not pairs and spec.value:
140
+ for pair_str in spec.value.split(","):
141
+ stripped = pair_str.strip()
142
+ if ":" in stripped:
143
+ k, v = stripped.split(":", 1)
144
+ pairs.append((k.strip(), v.strip()))
145
+ return pairs
146
+
147
+
148
+ def _build_metadata(
149
+ spec: ComposeSpec,
150
+ resolved: ResolvedArtifact,
151
+ duration_ms: float,
152
+ ) -> ArtifactMetadata:
153
+ return ArtifactMetadata(
154
+ type=spec.type,
155
+ genome=spec.genome_id,
156
+ profile=resolved.profile_id,
157
+ divider_variant=spec.divider_variant,
158
+ motion=resolved.motion,
159
+ state=spec.state,
160
+ regime=spec.regime,
161
+ width=resolved.width,
162
+ height=resolved.height,
163
+ metadata_tier=spec.metadata_tier,
164
+ duration_ms=round(duration_ms, 2),
165
+ generation=spec.generation,
166
+ series=spec.series,
167
+ )
@@ -0,0 +1,303 @@
1
+ """Centralized spatial-layout engine for badge / strip frames (v0.2.25).
2
+
3
+ Single source of truth for badge geometry. Replaces the inline arithmetic
4
+ that lived in ``compose/resolver.py:resolve_badge`` (lines 270-354) and
5
+ the duplicated geometry derivations in ``templates/frames/badge/*.j2``
6
+ (brutalist's right_x/right_w, cellular's inner_w/inner_off, the per-
7
+ template indicator_y duplicates).
8
+
9
+ Per Invariant 6 (CLAUDE.md): templates render, compose computes geometry.
10
+ Per ``feedback_compose_owns_geometry_template_renders.md``: layout
11
+ decisions belong in compose/, not Jinja2.
12
+
13
+ Two ``value_x`` regressions corrected by ``compute_badge_layout``:
14
+
15
+ 1. **Geometric centering, not midpoint of unrelated bounds.** The old
16
+ resolver used ``value_x = (right_x + val_pad_l + indicator_x) / 2``,
17
+ which is the midpoint of [value_zone_left, indicator_left_edge].
18
+ Indicator's left edge is val_min_gap inside the actual value-zone
19
+ right boundary. Result: text drifts ``val_min_gap/2 = 1.5px``
20
+ rightward. The new formula centers on the actual zone center.
21
+ 2. **No-indicator case is undefined under the old formula.** When the
22
+ indicator isn't rendered, ``indicator_x`` (still computed as
23
+ ``total_w - ind_pad_r - indicator_size``) lands *inside* or *left
24
+ of* the value text. The old midpoint averages two unrelated points.
25
+ The new formula uses ``total_w - val_min_gap`` as the right bound
26
+ when ``show_indicator=False`` and converges to a sane center.
27
+
28
+ Three orthogonal helpers live here so all layout-and-mode decisions
29
+ share a module:
30
+
31
+ - ``compute_badge_layout`` — geometry only; pure function over input
32
+ measurements and paradigm constants.
33
+ - ``resolve_badge_mode`` — three-mode classification
34
+ (stateful / stateless / explicit) keyed off the spec and a title
35
+ allowlist loaded from ``data/badge_modes.yaml``.
36
+ - ``decide_strip_mode`` — same classification rolled up over a strip's
37
+ metric labels.
38
+
39
+ The data-hw-statemode SVG-root attribute that gates threshold-CSS
40
+ auto-tinting in ``data/css/expression.css`` is derived from the badge
41
+ mode via ``data_hw_statemode_for``.
42
+ """
43
+
44
+ from __future__ import annotations
45
+
46
+ from dataclasses import dataclass
47
+ from typing import TYPE_CHECKING, Literal
48
+
49
+ if TYPE_CHECKING:
50
+ from collections.abc import Iterable
51
+
52
+ from hyperweave.core.models import ComposeSpec
53
+
54
+
55
+ BadgeMode = Literal["stateful", "stateless", "explicit"]
56
+
57
+
58
+ @dataclass(frozen=True)
59
+ class BadgeLayout:
60
+ """Resolved spatial layout for a badge frame.
61
+
62
+ Templates consume these directly via ``{{ width }}``, ``{{ label_x }}``,
63
+ ``{{ value_x }}``, etc. — no template-side arithmetic. The
64
+ ``value_zone_*`` fields are exported for unit-test assertions.
65
+ """
66
+
67
+ width: int
68
+ height: int
69
+ label_x: float
70
+ value_x: float
71
+ glyph_x: int
72
+ glyph_y: float
73
+ indicator_x: float
74
+ indicator_y: float
75
+ indicator_size: int
76
+ left_panel_w: int
77
+ right_panel_x: int
78
+ right_panel_w: int
79
+ value_zone_left: float
80
+ value_zone_right: float
81
+ value_zone_width: float
82
+ text_y: float
83
+ show_indicator: bool
84
+ inner_bit_w: int
85
+ inner_bit_offset: float
86
+
87
+
88
+ def compute_badge_layout(
89
+ *,
90
+ height: int,
91
+ measured_label_w: float,
92
+ measured_value_w: float,
93
+ has_glyph: bool,
94
+ show_indicator: bool,
95
+ use_mono: bool,
96
+ label_uppercase: bool,
97
+ value_raw_len: int,
98
+ accent_w: int,
99
+ inset: int,
100
+ glyph_size: int,
101
+ glyph_gap: int,
102
+ glyph_left_offset: int,
103
+ sep_w: int,
104
+ seam_w: int,
105
+ indicator_size: int,
106
+ ind_pad_r: int,
107
+ val_pad_l: int,
108
+ val_min_gap: int,
109
+ text_y_factor: float,
110
+ value_font_size: float,
111
+ right_canvas_inset: int = 0,
112
+ min_total_w: int = 60,
113
+ ) -> BadgeLayout:
114
+ """Compute every position the badge templates need from input measurements.
115
+
116
+ All positional fields in the returned ``BadgeLayout`` are measured
117
+ from the SVG origin (top-left). Geometry decisions (left/right panel
118
+ widths, indicator centering, glyph centering, value-zone centering)
119
+ are made here so all paradigms inherit the same correctness without
120
+ re-deriving in templates.
121
+
122
+ The centering correctness is verified by ``tests/test_badge_layout``
123
+ against short / long / version / license / python_requires values.
124
+ """
125
+ # Glyph pixel position. Mirrors resolver.py logic: paradigms with a
126
+ # left-edge decoration zone (cellular pattern strip x=2..~20) reserve
127
+ # space via ``glyph_left_offset``; brutalist/chrome declare 0.
128
+ if has_glyph:
129
+ glyph_x = (inset + accent_w + 4) if inset else (accent_w + 3)
130
+ glyph_x += glyph_left_offset
131
+ glyph_y = round((height - glyph_size) / 2, 1)
132
+ else:
133
+ glyph_x, glyph_y = 0, 0.0
134
+
135
+ # Label area starts after the glyph (or after the accent + paradigm
136
+ # left-edge decoration when there's no glyph). The no-glyph branch
137
+ # must respect glyph_left_offset too — cellular pattern strip runs
138
+ # to ~x=20 even when no glyph is rendered.
139
+ label_start = (glyph_x + glyph_size + glyph_gap) if has_glyph else (accent_w + 6 + glyph_left_offset)
140
+
141
+ # Left panel width — fits the label plus paradigm-driven right padding.
142
+ label_pad_r = 9 if use_mono else 8
143
+ left_panel = round(label_start + measured_label_w + label_pad_r)
144
+ left_panel = max(left_panel, 30)
145
+
146
+ # Label text center is the midpoint of the label area. For uppercase
147
+ # labels the visual right edge is 6px shy of the panel boundary so
148
+ # the cap glyphs don't kiss the seam.
149
+ label_area_end = left_panel - (6 if label_uppercase else 0)
150
+ label_x = round((label_start + label_area_end) / 2, 1)
151
+
152
+ # Right-panel allocation. ``ls_extra`` accounts for non-mono letter-
153
+ # spacing overrun (each char carries 0.4 of trailing tracking).
154
+ indicator_alloc = (indicator_size + ind_pad_r) if show_indicator else 0
155
+ ls_extra = value_raw_len * 0.4 if (not use_mono and value_raw_len) else 0
156
+ right_panel = val_pad_l + measured_value_w + ls_extra + 2 * val_min_gap + indicator_alloc
157
+ total_w = round(left_panel + sep_w + seam_w + right_panel)
158
+ total_w = max(total_w, min_total_w)
159
+
160
+ right_panel_x = left_panel + sep_w + seam_w
161
+ right_panel_w = total_w - right_panel_x
162
+
163
+ # Indicator position. Computed even when not shown — templates
164
+ # gate the actual rendering on ``show_indicator``; the value-zone
165
+ # center collapses to total_w-val_min_gap when the indicator is
166
+ # absent so the value text reclaims the freed space.
167
+ indicator_x = total_w - ind_pad_r - indicator_size
168
+
169
+ # Value-zone bounds — the heart of the v0.2.25 centering fix. The
170
+ # OLD resolver used ``(right_panel_x + val_pad_l + indicator_x) / 2``
171
+ # which is the midpoint of [zone_left, indicator_left_edge], not
172
+ # [zone_left, zone_right]. Old formula skewed text by val_min_gap/2
173
+ # toward the indicator and was undefined when no indicator rendered.
174
+ value_zone_left = right_panel_x + val_pad_l
175
+ # When stateless (no indicator), the slab's right edge is total_w minus
176
+ # any paradigm-specific canvas inset (cellular: 2; brutalist/chrome: 0).
177
+ # val_min_gap is the symmetric trailing pad — together they place
178
+ # value_zone_right at the slab's interior right boundary so a centered
179
+ # text lands at slab geometric center.
180
+ value_zone_right = (indicator_x - val_min_gap) if show_indicator else (total_w - right_canvas_inset - val_min_gap)
181
+ value_zone_width = value_zone_right - value_zone_left
182
+ value_x = round((value_zone_left + value_zone_right) / 2, 1)
183
+
184
+ text_y = round(height * text_y_factor, 1)
185
+ # Indicator vertical center pinned to value-text visual midline.
186
+ # cap_height ≈ 70% of font_size across the genome's display fonts
187
+ # (validated in data/font-metrics/), so visual_center = text_y -
188
+ # 0.3 * font_size. Indicator is square; top-y = visual_center -
189
+ # size/2. Single source of truth for vertical alignment across
190
+ # paradigms.
191
+ indicator_y = round(text_y - value_font_size * 0.3 - indicator_size / 2, 1)
192
+
193
+ # Indicator inner-bit geometry — moved from cellular/brutalist
194
+ # template-side arithmetic. The bit is half the indicator side,
195
+ # centered.
196
+ inner_bit_w = indicator_size // 2
197
+ inner_bit_offset = (indicator_size - inner_bit_w) / 2
198
+
199
+ return BadgeLayout(
200
+ width=total_w,
201
+ height=height,
202
+ label_x=label_x,
203
+ value_x=value_x,
204
+ glyph_x=glyph_x,
205
+ glyph_y=glyph_y,
206
+ indicator_x=indicator_x,
207
+ indicator_y=indicator_y,
208
+ indicator_size=indicator_size,
209
+ left_panel_w=left_panel,
210
+ right_panel_x=right_panel_x,
211
+ right_panel_w=right_panel_w,
212
+ value_zone_left=value_zone_left,
213
+ value_zone_right=value_zone_right,
214
+ value_zone_width=value_zone_width,
215
+ text_y=text_y,
216
+ show_indicator=show_indicator,
217
+ inner_bit_w=inner_bit_w,
218
+ inner_bit_offset=inner_bit_offset,
219
+ )
220
+
221
+
222
+ # ─────────────────────────────────────────────────────────────────────
223
+ # Three-mode state architecture
224
+ # ─────────────────────────────────────────────────────────────────────
225
+
226
+
227
+ def normalize_title(title: str | None) -> str:
228
+ """Lowercase + strip hyphens/underscores so allowlist lookup is
229
+ insensitive to common separator variants.
230
+
231
+ ``BUILD-STATUS`` → ``buildstatus``; ``CI_CD`` → ``cicd``. URL slashes
232
+ can't appear in path segments (they'd split into separate parts), so
233
+ we don't need slash handling. Empty / None → empty string.
234
+ """
235
+ if not title:
236
+ return ""
237
+ return title.lower().replace("-", "").replace("_", "")
238
+
239
+
240
+ def resolve_badge_mode(spec: ComposeSpec, allowlist: frozenset[str]) -> BadgeMode:
241
+ """Classify a badge as stateful / stateless / explicit.
242
+
243
+ Three modes drive two orthogonal behaviors at render time:
244
+
245
+ * Indicator rendering: ``show_indicator = mode != "stateless"``
246
+ * Threshold-CSS auto-inference: gated by ``data-hw-statemode="auto"``
247
+ on the SVG root, which fires only for ``stateful`` (auto-inferred
248
+ from leading-digit value). ``stateless`` and ``explicit`` skip it.
249
+
250
+ Title lookup normalizes via ``normalize_title`` (lowercase, strip
251
+ hyphens/underscores) so ``BUILD-STATUS`` and ``BUILD_STATUS`` both
252
+ match the canonical ``buildstatus`` allowlist entry without bloating
253
+ the YAML with every separator variant.
254
+
255
+ Note on ``spec.state == "active"``: ComposeSpec defaults ``state`` to
256
+ the truthy sentinel ``"active"`` (Pydantic default in
257
+ core/models.py:98), NOT empty string. Treat that sentinel as "user
258
+ did not opine" — fall through to the allowlist check. Any other
259
+ value (including ``"active"`` if explicitly re-set, which is fine)
260
+ means the caller asked for a specific state → explicit mode.
261
+ """
262
+ if spec.state and spec.state != "active":
263
+ return "explicit"
264
+ title = normalize_title(spec.title)
265
+ if title and title in allowlist:
266
+ return "stateful"
267
+ return "stateless"
268
+
269
+
270
+ def decide_strip_mode(
271
+ metric_titles: Iterable[str | None],
272
+ spec: ComposeSpec,
273
+ allowlist: frozenset[str],
274
+ ) -> BadgeMode:
275
+ """Roll up the strip's mode from its metric cells' titles.
276
+
277
+ Strip's right-edge indicator is the strip's overall health pixel.
278
+ If ANY metric is stateful, the indicator renders with rolled-up
279
+ state. Stateless cells coexist; per-cell indicators were already
280
+ rejected (memory: ``feedback_strip_single_diamond.md``).
281
+
282
+ Metric titles are normalized via ``normalize_title`` for the same
283
+ reasons as ``resolve_badge_mode`` — ``BUILD-STATUS`` cell matches
284
+ the same canonical ``buildstatus`` allowlist entry.
285
+ """
286
+ if spec.state and spec.state != "active":
287
+ return "explicit"
288
+ for title in metric_titles:
289
+ if title and normalize_title(title) in allowlist:
290
+ return "stateful"
291
+ return "stateless"
292
+
293
+
294
+ def data_hw_statemode_for(mode: BadgeMode) -> str:
295
+ """Map ``BadgeMode`` to the SVG-root ``data-hw-statemode`` attribute value.
296
+
297
+ The CSS in ``data/css/expression.css`` qualifies its threshold
298
+ selectors with ``[data-hw-statemode="auto"]`` so auto-inference
299
+ only applies to ``stateful``. ``stateless`` ("off") and ``explicit``
300
+ ("explicit") bypass auto-tinting; explicit-mode badges still get
301
+ state colors via the ``[data-hw-status="..."]`` cascade.
302
+ """
303
+ return {"stateful": "auto", "explicit": "explicit", "stateless": "off"}[mode]