hyperweave 0.3.5__tar.gz → 0.3.6__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 (422) hide show
  1. {hyperweave-0.3.5 → hyperweave-0.3.6}/CHANGELOG.md +23 -0
  2. {hyperweave-0.3.5 → hyperweave-0.3.6}/PKG-INFO +1 -1
  3. {hyperweave-0.3.5 → hyperweave-0.3.6}/justfile +10 -0
  4. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/_version.py +2 -2
  5. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/cli.py +178 -44
  6. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/context.py +1 -1
  7. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/resolver.py +47 -19
  8. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/telemetry/codex_parser.py +5 -0
  9. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/telemetry/contract.py +5 -0
  10. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/telemetry/models.py +12 -0
  11. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/telemetry/parser.py +6 -0
  12. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_doctor.py +53 -4
  13. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_install_hook.py +122 -7
  14. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_telemetry_integration.py +70 -1
  15. {hyperweave-0.3.5 → hyperweave-0.3.6}/.dockerignore +0 -0
  16. {hyperweave-0.3.5 → hyperweave-0.3.6}/.github/workflows/ci.yml +0 -0
  17. {hyperweave-0.3.5 → hyperweave-0.3.6}/.github/workflows/deploy.yml +0 -0
  18. {hyperweave-0.3.5 → hyperweave-0.3.6}/.github/workflows/publish.yml +0 -0
  19. {hyperweave-0.3.5 → hyperweave-0.3.6}/.gitignore +0 -0
  20. {hyperweave-0.3.5 → hyperweave-0.3.6}/.pre-commit-config.yaml +0 -0
  21. {hyperweave-0.3.5 → hyperweave-0.3.6}/Dockerfile +0 -0
  22. {hyperweave-0.3.5 → hyperweave-0.3.6}/LICENSE +0 -0
  23. {hyperweave-0.3.5 → hyperweave-0.3.6}/README.md +0 -0
  24. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/buttons/button-liquid.svg +0 -0
  25. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/cards/card-butterfly.svg +0 -0
  26. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/cards/card-python.svg +0 -0
  27. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/cards/card-sunflower.svg +0 -0
  28. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/cards/card-waves.svg +0 -0
  29. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/badge_critical.svg +0 -0
  30. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/badge_passing.svg +0 -0
  31. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/badge_warning.svg +0 -0
  32. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/banner.svg +0 -0
  33. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/icons/discord.svg +0 -0
  34. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/icons/notion.svg +0 -0
  35. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/icons/reddit.svg +0 -0
  36. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/icons/spotify.svg +0 -0
  37. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/icons/youtube.svg +0 -0
  38. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/marquee_counter.svg +0 -0
  39. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/marquee_horizontal.svg +0 -0
  40. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/marquee_vertical.svg +0 -0
  41. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/profile-cards/chart_stars_full.svg +0 -0
  42. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/profile-cards/stats.svg +0 -0
  43. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/strip.svg +0 -0
  44. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/badge_critical.svg +0 -0
  45. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/badge_passing.svg +0 -0
  46. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/badge_warning.svg +0 -0
  47. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/banner.svg +0 -0
  48. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/icons/bluesky.svg +0 -0
  49. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/icons/github.svg +0 -0
  50. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/icons/instagram.svg +0 -0
  51. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/icons/mastodon.svg +0 -0
  52. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/icons/x.svg +0 -0
  53. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/marquee_counter.svg +0 -0
  54. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/marquee_horizontal.svg +0 -0
  55. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/marquee_vertical.svg +0 -0
  56. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/profile-cards/chart_stars_full.svg +0 -0
  57. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/profile-cards/stats.svg +0 -0
  58. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/strip.svg +0 -0
  59. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/telemetry/master_card.svg +0 -0
  60. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/telemetry/receipt.svg +0 -0
  61. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/telemetry/receipt_claude-code_medium.svg +0 -0
  62. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/telemetry/receipt_codex_large.svg +0 -0
  63. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/telemetry/receipt_voltage_xlarge.svg +0 -0
  64. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/telemetry/rhythm_strip.svg +0 -0
  65. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/telemetry/rhythm_strip_voltage_xlarge.svg +0 -0
  66. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/footers/inneraura-footer-liquid.svg +0 -0
  67. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/footers/inneraura-footer-purple.svg +0 -0
  68. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/footers/inneraura-footer.svg +0 -0
  69. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/hyperweave-banner.svg +0 -0
  70. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/icons/cobalt-sapphire-discord.svg +0 -0
  71. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/icons/cobalt-sapphire-docs.svg +0 -0
  72. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/icons/cobalt-sapphire-github.svg +0 -0
  73. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/icons/cobalt-sapphire-instagram.svg +0 -0
  74. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/icons/cobalt-sapphire-linkedin.svg +0 -0
  75. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/icons/cobalt-sapphire-tiktok.svg +0 -0
  76. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/icons/cobalt-sapphire-x.svg +0 -0
  77. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/icons/cobalt-sapphire-youtube.svg +0 -0
  78. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/marquees/badge-showcase-triple.svg +0 -0
  79. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/marquees/genome-marquee-triple.svg +0 -0
  80. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/marquees/sample-badges.svg +0 -0
  81. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/mintlify-assets/404.svg +0 -0
  82. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/mintlify-assets/callout-icons.svg +0 -0
  83. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/mintlify-assets/divider.svg +0 -0
  84. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/mintlify-assets/favicon.svg +0 -0
  85. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/mintlify-assets/hero-banner.svg +0 -0
  86. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/mintlify-assets/hyperweave-banner-v2.svg +0 -0
  87. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/mintlify-assets/hyperweave-navbar-logo.svg +0 -0
  88. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/mintlify-assets/loader.svg +0 -0
  89. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/mintlify-assets/og-image.svg +0 -0
  90. {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/timelines/hyperweave-roadmap.svg +0 -0
  91. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/assets/favicon.svg +0 -0
  92. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/assets/hyperweave-banner.svg +0 -0
  93. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/assets/hyperweave-navbar-logo.svg +0 -0
  94. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/assets/og-image.svg +0 -0
  95. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/explanation/architecture.mdx +0 -0
  96. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/explanation/camo-compatibility.mdx +0 -0
  97. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/explanation/cim-compliance.mdx +0 -0
  98. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/explanation/genome-profile-system.mdx +0 -0
  99. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/explanation/metadata-tiers.mdx +0 -0
  100. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/how-to/add-motion-to-badges.mdx +0 -0
  101. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/how-to/create-session-receipts.mdx +0 -0
  102. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/how-to/set-up-claude-code-hooks.mdx +0 -0
  103. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/how-to/use-live-data-badges.mdx +0 -0
  104. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/introduction.mdx +0 -0
  105. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/mint.json +0 -0
  106. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/quickstart.mdx +0 -0
  107. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/reference/cli.mdx +0 -0
  108. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/reference/compose-spec.mdx +0 -0
  109. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/reference/genomes.mdx +0 -0
  110. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/reference/http-api.mdx +0 -0
  111. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/reference/mcp-tools.mdx +0 -0
  112. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/reference/motions.mdx +0 -0
  113. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/reference/telemetry-contract.mdx +0 -0
  114. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/spec/hyperweave-protocol.mdx +0 -0
  115. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/tutorials/build-an-artifact-kit.mdx +0 -0
  116. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/tutorials/compose-your-first-badge.mdx +0 -0
  117. {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/tutorials/install-session-telemetry.mdx +0 -0
  118. {hyperweave-0.3.5 → hyperweave-0.3.6}/fly.toml +0 -0
  119. {hyperweave-0.3.5 → hyperweave-0.3.6}/hooks/install.py +0 -0
  120. {hyperweave-0.3.5 → hyperweave-0.3.6}/hooks/session_end.sh +0 -0
  121. {hyperweave-0.3.5 → hyperweave-0.3.6}/pyproject.toml +0 -0
  122. {hyperweave-0.3.5 → hyperweave-0.3.6}/scripts/extract_font_metrics.py +0 -0
  123. {hyperweave-0.3.5 → hyperweave-0.3.6}/scripts/extract_glyphs.py +0 -0
  124. {hyperweave-0.3.5 → hyperweave-0.3.6}/scripts/generate_proofset.py +0 -0
  125. {hyperweave-0.3.5 → hyperweave-0.3.6}/scripts/probe_live_stats.py +0 -0
  126. {hyperweave-0.3.5 → hyperweave-0.3.6}/scripts/probe_star_history.py +0 -0
  127. {hyperweave-0.3.5 → hyperweave-0.3.6}/scripts/stress_test.py +0 -0
  128. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/__init__.py +0 -0
  129. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/__main__.py +0 -0
  130. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/__init__.py +0 -0
  131. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/assembler.py +0 -0
  132. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/bar_chart.py +0 -0
  133. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/engine.py +0 -0
  134. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/lanes.py +0 -0
  135. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/layout.py +0 -0
  136. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/palette.py +0 -0
  137. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/reasoning.py +0 -0
  138. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/resolvers/__init__.py +0 -0
  139. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/resolvers/chart.py +0 -0
  140. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/resolvers/stats.py +0 -0
  141. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/rhythm.py +0 -0
  142. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/rhythm_strip.py +0 -0
  143. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/treemap.py +0 -0
  144. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/validate_paradigms.py +0 -0
  145. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/config/__init__.py +0 -0
  146. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/config/genome_validator.py +0 -0
  147. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/config/loader.py +0 -0
  148. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/config/registry.py +0 -0
  149. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/config/settings.py +0 -0
  150. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/connectors/__init__.py +0 -0
  151. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/connectors/arxiv.py +0 -0
  152. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/connectors/base.py +0 -0
  153. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/connectors/cache.py +0 -0
  154. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/connectors/github.py +0 -0
  155. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/connectors/rest.py +0 -0
  156. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/core/__init__.py +0 -0
  157. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/core/cell_layout.py +0 -0
  158. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/core/color.py +0 -0
  159. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/core/contracts.py +0 -0
  160. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/core/enums.py +0 -0
  161. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/core/font_metrics.py +0 -0
  162. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/core/models.py +0 -0
  163. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/core/paradigm.py +0 -0
  164. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/core/schema.py +0 -0
  165. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/core/state.py +0 -0
  166. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/core/text.py +0 -0
  167. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/core/thresholds.py +0 -0
  168. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/badge_modes.yaml +0 -0
  169. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/css/accessibility.css +0 -0
  170. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/css/bridge.css +0 -0
  171. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/css/expression.css +0 -0
  172. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/css/status.css +0 -0
  173. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/font-metrics/chakra-petch.json +0 -0
  174. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/font-metrics/inter.json +0 -0
  175. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/font-metrics/jetbrains-mono.json +0 -0
  176. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/font-metrics/orbitron.json +0 -0
  177. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/fonts/barlow-condensed-700.b64 +0 -0
  178. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/fonts/barlow-condensed-700.meta.json +0 -0
  179. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/fonts/barlow-condensed-900.b64 +0 -0
  180. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/fonts/barlow-condensed-900.meta.json +0 -0
  181. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/fonts/chakra-petch.b64 +0 -0
  182. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/fonts/chakra-petch.meta.json +0 -0
  183. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/fonts/jetbrains-mono.b64 +0 -0
  184. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/fonts/jetbrains-mono.meta.json +0 -0
  185. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/fonts/orbitron.b64 +0 -0
  186. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/fonts/orbitron.meta.json +0 -0
  187. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/genomes/automata.json +0 -0
  188. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/genomes/brutalist.json +0 -0
  189. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/genomes/chrome.json +0 -0
  190. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/genomes/telemetry-claude-code.json +0 -0
  191. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/genomes/telemetry-codex.json +0 -0
  192. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/genomes/telemetry-cream.json +0 -0
  193. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/genomes/telemetry-voltage.json +0 -0
  194. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/glyphs.json +0 -0
  195. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/motions/border/chromatic-pulse.yaml +0 -0
  196. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/motions/border/corner-trace.yaml +0 -0
  197. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/motions/border/dual-orbit.yaml +0 -0
  198. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/motions/border/entanglement.yaml +0 -0
  199. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/motions/border/rimrun.yaml +0 -0
  200. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/motions/static.yaml +0 -0
  201. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/paradigms/brutalist.yaml +0 -0
  202. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/paradigms/cellular.yaml +0 -0
  203. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/paradigms/chrome.yaml +0 -0
  204. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/paradigms/default.yaml +0 -0
  205. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/policies/normal.json +0 -0
  206. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/policies/permissive.json +0 -0
  207. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/policies/ungoverned.json +0 -0
  208. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/profiles/brutalist.contract.json +0 -0
  209. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/profiles/brutalist.yaml +0 -0
  210. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/profiles/chrome.contract.json +0 -0
  211. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/profiles/chrome.yaml +0 -0
  212. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/reasoning/__init__.py +0 -0
  213. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/reasoning/brutalist.yaml +0 -0
  214. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/specimens.yaml +0 -0
  215. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/telemetry/model-pricing.yaml +0 -0
  216. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/telemetry/runtimes/claude-code.yaml +0 -0
  217. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/telemetry/runtimes/codex.yaml +0 -0
  218. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/telemetry/stage-config.yaml +0 -0
  219. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/telemetry/stage-labels.yaml +0 -0
  220. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/telemetry/tool-colors.yaml +0 -0
  221. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/arrow.json +0 -0
  222. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/aurora.json +0 -0
  223. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/beacon.json +0 -0
  224. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/bracket3d.json +0 -0
  225. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/chevron.json +0 -0
  226. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/crosshair.json +0 -0
  227. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/diamond.json +0 -0
  228. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/rocket.json +0 -0
  229. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/rules/bar.json +0 -0
  230. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/rules/dashed.json +0 -0
  231. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/rules/five-wave.json +0 -0
  232. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/rules/spectral.json +0 -0
  233. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/rules/straight.json +0 -0
  234. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/rules/wave.json +0 -0
  235. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/stijl.json +0 -0
  236. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/kit.py +0 -0
  237. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/mcp/__init__.py +0 -0
  238. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/mcp/__main__.py +0 -0
  239. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/mcp/server.py +0 -0
  240. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/py.typed +0 -0
  241. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/render/__init__.py +0 -0
  242. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/render/chart_engine.py +0 -0
  243. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/render/fonts.py +0 -0
  244. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/render/glyphs.py +0 -0
  245. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/render/motion.py +0 -0
  246. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/render/templates.py +0 -0
  247. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/serve/__init__.py +0 -0
  248. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/serve/app.py +0 -0
  249. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/serve/data_tokens.py +0 -0
  250. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/telemetry/__init__.py +0 -0
  251. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/telemetry/capture.py +0 -0
  252. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/telemetry/corrections.py +0 -0
  253. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/telemetry/cost.py +0 -0
  254. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/telemetry/receipt_paths.py +0 -0
  255. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/telemetry/runtimes.py +0 -0
  256. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/telemetry/stages.py +0 -0
  257. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/chart-area.svg.j2 +0 -0
  258. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/chart-axes.svg.j2 +0 -0
  259. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/chart-empty-state.svg.j2 +0 -0
  260. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/chart-gridlines.svg.j2 +0 -0
  261. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/chart-markers/circle.svg.j2 +0 -0
  262. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/chart-markers/diamond.svg.j2 +0 -0
  263. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/chart-markers/endpoint-diamond.svg.j2 +0 -0
  264. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/chart-markers/endpoint-rect.svg.j2 +0 -0
  265. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/chart-markers/rect.svg.j2 +0 -0
  266. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/chart-milestone-brutalist.svg.j2 +0 -0
  267. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/chart-milestone.svg.j2 +0 -0
  268. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/chart-polyline.svg.j2 +0 -0
  269. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/filter.svg.j2 +0 -0
  270. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/glyph-inline.svg.j2 +0 -0
  271. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/glyph.svg.j2 +0 -0
  272. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/gradient.svg.j2 +0 -0
  273. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/metadata.svg.j2 +0 -0
  274. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/metric.svg.j2 +0 -0
  275. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/phase-segment.svg.j2 +0 -0
  276. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/rule.svg.j2 +0 -0
  277. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/status.svg.j2 +0 -0
  278. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/terminal.svg.j2 +0 -0
  279. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/document.svg.j2 +0 -0
  280. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/error-badge.svg.j2 +0 -0
  281. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/badge/brutalist-content.j2 +0 -0
  282. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/badge/brutalist-dark-content.j2 +0 -0
  283. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/badge/brutalist-defs.j2 +0 -0
  284. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/badge/brutalist-light-content.j2 +0 -0
  285. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/badge/cellular-content.j2 +0 -0
  286. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/badge/cellular-defs.j2 +0 -0
  287. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/badge/chrome-content.j2 +0 -0
  288. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/badge/chrome-defs.j2 +0 -0
  289. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/badge/default-content.j2 +0 -0
  290. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/badge/default-defs.j2 +0 -0
  291. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/badge.svg.j2 +0 -0
  292. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/catalog.svg.j2 +0 -0
  293. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/chart/brutalist-content.j2 +0 -0
  294. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/chart/brutalist-dark-content.j2 +0 -0
  295. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/chart/brutalist-defs.j2 +0 -0
  296. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/chart/brutalist-light-content.j2 +0 -0
  297. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/chart/cellular-content.j2 +0 -0
  298. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/chart/cellular-defs.j2 +0 -0
  299. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/chart/chrome-content.j2 +0 -0
  300. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/chart/chrome-defs.j2 +0 -0
  301. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/chart.svg.j2 +0 -0
  302. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/divider/brutalist-seam.svg.j2 +0 -0
  303. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/divider/chrome-band.svg.j2 +0 -0
  304. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/divider.svg.j2 +0 -0
  305. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/icon/brutalist-content.j2 +0 -0
  306. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/icon/brutalist-defs.j2 +0 -0
  307. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/icon/cellular-content.j2 +0 -0
  308. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/icon/cellular-defs.j2 +0 -0
  309. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/icon/chrome-content.j2 +0 -0
  310. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/icon/chrome-defs.j2 +0 -0
  311. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/icon/default-content.j2 +0 -0
  312. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/icon/default-defs.j2 +0 -0
  313. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/icon.svg.j2 +0 -0
  314. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/marquee-horizontal/brutalist-content.j2 +0 -0
  315. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/marquee-horizontal/brutalist-defs.j2 +0 -0
  316. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/marquee-horizontal/brutalist-overlay.j2 +0 -0
  317. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/marquee-horizontal/cellular-content.j2 +0 -0
  318. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/marquee-horizontal/cellular-defs.j2 +0 -0
  319. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/marquee-horizontal/cellular-overlay.j2 +0 -0
  320. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/marquee-horizontal/chrome-content.j2 +0 -0
  321. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/marquee-horizontal/chrome-defs.j2 +0 -0
  322. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/marquee-horizontal/chrome-overlay.j2 +0 -0
  323. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/marquee-horizontal/default-content.j2 +0 -0
  324. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/marquee-horizontal/default-defs.j2 +0 -0
  325. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/marquee-horizontal.svg.j2 +0 -0
  326. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/master-card.svg.j2 +0 -0
  327. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/receipt.svg.j2 +0 -0
  328. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/rhythm-strip.svg.j2 +0 -0
  329. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/stats/brutalist-content.j2 +0 -0
  330. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/stats/brutalist-dark-content.j2 +0 -0
  331. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/stats/brutalist-defs.j2 +0 -0
  332. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/stats/brutalist-light-content.j2 +0 -0
  333. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/stats/cellular-content.j2 +0 -0
  334. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/stats/cellular-defs.j2 +0 -0
  335. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/stats/chrome-content.j2 +0 -0
  336. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/stats/chrome-defs.j2 +0 -0
  337. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/stats.svg.j2 +0 -0
  338. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/brutalist-content.j2 +0 -0
  339. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/brutalist-dark-content.j2 +0 -0
  340. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/brutalist-defs.j2 +0 -0
  341. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/brutalist-light-content.j2 +0 -0
  342. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/brutalist-status.j2 +0 -0
  343. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/cellular-content.j2 +0 -0
  344. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/cellular-defs.j2 +0 -0
  345. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/cellular-status.j2 +0 -0
  346. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/chrome-content.j2 +0 -0
  347. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/chrome-defs.j2 +0 -0
  348. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/chrome-status.j2 +0 -0
  349. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/default-content.j2 +0 -0
  350. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/default-defs.j2 +0 -0
  351. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/default-status.j2 +0 -0
  352. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip.svg.j2 +0 -0
  353. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/motions/border/chromatic-pulse.svg.j2 +0 -0
  354. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/motions/border/corner-trace.svg.j2 +0 -0
  355. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/motions/border/dual-orbit.svg.j2 +0 -0
  356. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/motions/border/entanglement.svg.j2 +0 -0
  357. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/motions/border/rimrun.svg.j2 +0 -0
  358. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/partials/provider-glyphs.svg.j2 +0 -0
  359. {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/partials/state-signal-cascade.j2 +0 -0
  360. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/__init__.py +0 -0
  361. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/conftest.py +0 -0
  362. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/fixtures/codex_session.jsonl +0 -0
  363. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/fixtures/codex_session_patches.jsonl +0 -0
  364. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/fixtures/github_contributions/synthetic.html +0 -0
  365. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/fixtures/session.json +0 -0
  366. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/fixtures/session.jsonl +0 -0
  367. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/fixtures/synthetic_session.jsonl +0 -0
  368. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/helpers.py +0 -0
  369. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/snapshots/url_stability/divider_band_chrome_static.svg +0 -0
  370. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/snapshots/url_stability/icon_github_chrome_static.svg +0 -0
  371. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_assembler.py +0 -0
  372. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_badge_brutalist_dark.py +0 -0
  373. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_badge_cellular.py +0 -0
  374. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_badge_layout.py +0 -0
  375. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_badge_mode.py +0 -0
  376. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_chart_engine.py +0 -0
  377. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_chart_frame.py +0 -0
  378. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_chromatic_coverage_validator.py +0 -0
  379. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_chrome_variants.py +0 -0
  380. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_codex_parser.py +0 -0
  381. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_compose_bar_chart.py +0 -0
  382. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_compose_rhythm_strip.py +0 -0
  383. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_compose_treemap.py +0 -0
  384. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_compose_variant_field.py +0 -0
  385. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_connectors.py +0 -0
  386. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_core.py +0 -0
  387. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_data_tokens.py +0 -0
  388. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_divider_cellular.py +0 -0
  389. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_divider_genome_specific.py +0 -0
  390. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_font_gating.py +0 -0
  391. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_font_metrics_chakra.py +0 -0
  392. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_genome_automata.py +0 -0
  393. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_github_scrape.py +0 -0
  394. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_kit.py +0 -0
  395. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_marquee_v0_2_16.py +0 -0
  396. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_mcp.py +0 -0
  397. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_paradigm_cellular.py +0 -0
  398. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_paradigm_dispatch.py +0 -0
  399. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_paradigm_extensibility.py +0 -0
  400. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_parse_transcript_auto.py +0 -0
  401. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_proofset.py +0 -0
  402. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_receipt_paths.py +0 -0
  403. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_receipt_token_compliance.py +0 -0
  404. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_render.py +0 -0
  405. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_resolver_rhythm.py +0 -0
  406. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_runtime_registries.py +0 -0
  407. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_serve.py +0 -0
  408. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_serve_live_state.py +0 -0
  409. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_slot_metric_state.py +0 -0
  410. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_state_inference.py +0 -0
  411. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_stats_brutalist.py +0 -0
  412. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_stats_card.py +0 -0
  413. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_strip_brutalist_grammar.py +0 -0
  414. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_strip_cellular.py +0 -0
  415. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_strip_status_toggle.py +0 -0
  416. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_telemetry.py +0 -0
  417. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_telemetry_contract_per_stage.py +0 -0
  418. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_telemetry_skin_dispatch.py +0 -0
  419. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_tool_class_completeness.py +0 -0
  420. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_url_stability.py +0 -0
  421. {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_variant_default_resolution.py +0 -0
  422. {hyperweave-0.3.5 → hyperweave-0.3.6}/uv.lock +0 -0
@@ -5,6 +5,29 @@ 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.3.6] - 2026-05-18
9
+
10
+ Receipt active-duration sources from per-turn compute when the runtime emits it; long-running sessions no longer inflate via stage-span summation. Codex hooks migrate to the wrapped-matcher GA layout shipped in the Codex CLI v0.129 release. The `data-hw-genome` root attribute reflects the resolved genome ID instead of raw spec input.
11
+
12
+ ### Added
13
+
14
+ - **`just tag VERSION MESSAGE`** — annotated tag plus automatic `_version.py` refresh so the `doctor` banner and metadata version stay in sync after release.
15
+ - **`just version-refresh`** — standalone recipe to rebuild `_version.py` from the current git tag.
16
+
17
+ ### Changed
18
+
19
+ - **Receipt active-duration** — Claude Code sessions source from `system.turn_duration` events; Codex and mocked fixtures fall back to the prior `min(stage-span sum, wall-clock span)` formula.
20
+ - **Session contract** — new `turn_duration_minutes` field surfaces the parser's per-turn compute sum; null when the runtime emits no per-turn signal.
21
+ - **Codex hooks file** — `install-hook` writes the wrapped-matcher GA format (Codex CLI v0.129+). Existing flat installs are migrated on next install.
22
+ - **Hooks feature flag** — `codex_hooks` renames to `hooks` to match Codex's GA terminology; the legacy name continues to work for one release.
23
+ - **`hyperweave doctor`** — traverses both the new wrapped format and the legacy flat layout, surfacing a migration prompt when a flat config is detected.
24
+ - **Genome root attribute** — `data-hw-genome` reflects the resolved genome ID instead of raw spec input, so variants and auto-resolved genomes never emit empty.
25
+
26
+ ### Notes
27
+
28
+ - Codex sessions hit the active-duration fallback because the runtime emits no per-turn duration signal.
29
+ - `_version.py` refreshes automatically when tagging through `just tag`.
30
+
8
31
  ## [0.3.5] - 2026-05-17
9
32
 
10
33
  Receipt hero reads `token volume · $cost` with a four-cell decomposition strip below (IN / OUT / CACHED / WRITTEN), making the asymmetric cache-token math legible at a glance. Codex receipts now price tokens against the OpenAI rate card; figures previously inflated ~2× drop to actual GPT rates.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperweave
3
- Version: 0.3.5
3
+ Version: 0.3.6
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
@@ -48,3 +48,13 @@ docs:
48
48
 
49
49
  build:
50
50
  uv build
51
+
52
+ version-refresh:
53
+ uv pip install -e . --force-reinstall --no-deps --quiet
54
+ @uv run python -c "import hyperweave; print(f'_version.py refreshed to {hyperweave.__version__}')"
55
+
56
+ tag VERSION MESSAGE:
57
+ git tag -a {{VERSION}} -m "{{MESSAGE}}"
58
+ just version-refresh
59
+ @echo ""
60
+ @echo "Tagged {{VERSION}}. Push with: git push --follow-tags"
@@ -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.3.5'
22
- __version_tuple__ = version_tuple = (0, 3, 5)
21
+ __version__ = version = '0.3.6'
22
+ __version_tuple__ = version_tuple = (0, 3, 6)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -567,8 +567,45 @@ def _install_claude_code_hook(hook_command: str, full_slug: str) -> None:
567
567
  typer.echo(f"Installed SessionEnd hook in {settings_path}{pinned}")
568
568
 
569
569
 
570
+ # Codex hook event names (v0.129.0 GA). Used by ``_install_codex_hook`` and
571
+ # ``_doctor_runtime_status`` to detect pre-GA flat-format event keys sitting
572
+ # at the top of ``hooks.json`` and lift them under the canonical ``hooks``
573
+ # wrapper introduced when hooks went GA.
574
+ _CODEX_HOOK_EVENTS = frozenset(
575
+ {
576
+ "SessionStart",
577
+ "PreToolUse",
578
+ "PermissionRequest",
579
+ "PostToolUse",
580
+ "UserPromptSubmit",
581
+ "Stop",
582
+ }
583
+ )
584
+
585
+
586
+ def _wrap_legacy_codex_hook_entry(entry: object) -> object:
587
+ """Lift a pre-GA bare-command hook entry into the GA matcher+hooks group.
588
+
589
+ Codex v0.129 (hooks GA) changed each event-array entry from a bare
590
+ ``{type, command, timeout}`` to a matcher group
591
+ ``{matcher, hooks: [{type, command, timeout}]}``. Idempotent: an entry
592
+ already carrying a list-valued ``hooks`` key is returned unchanged so
593
+ repeated migrations stay stable. Bare-command entries are wrapped under
594
+ a universal ``"*"`` matcher; the matcher is parsed-but-ignored for Stop
595
+ today per the spec, but we use ``"*"`` for forward-compat against any
596
+ future Codex release that begins to honor it on Stop.
597
+ """
598
+ if not isinstance(entry, dict):
599
+ return entry
600
+ if isinstance(entry.get("hooks"), list):
601
+ return entry # already in GA shape
602
+ if entry.get("type") == "command":
603
+ return {"matcher": "*", "hooks": [entry]}
604
+ return entry # unknown shape — preserve as-is
605
+
606
+
570
607
  def _install_codex_hook(hook_command: str, full_slug: str) -> None:
571
- """Write a Stop hook to ``~/.codex/hooks.json`` + enable codex_hooks feature.
608
+ """Write a Stop hook to ``~/.codex/hooks.json`` + enable hooks feature.
572
609
 
573
610
  Per developers.openai.com/codex/hooks, Codex CLI fires Stop hooks
574
611
  PER-TURN (after every assistant response). The receipt rewrites the
@@ -579,9 +616,21 @@ def _install_codex_hook(hook_command: str, full_slug: str) -> None:
579
616
  will lean into live-receipt consumers (file watchers, dashboards)
580
617
  rather than collapse it back to a single session-end event.
581
618
 
582
- Also writes ``[features] codex_hooks = true`` to ``~/.codex/config.toml``
583
- (preserving any other keys); the feature flag is required for the
584
- hook system to fire.
619
+ Codex v0.129.0 (2026-05-07) took hooks GA with two shape changes the
620
+ installer handles via migration-on-write:
621
+
622
+ * ``hooks.json`` moved from flat ``{Stop: [{type, command, timeout}]}``
623
+ to wrapped ``{hooks: {Stop: [{matcher, hooks: [{type, command,
624
+ timeout}]}]}}``. Any pre-GA event keys at the top level are lifted
625
+ under the new wrapper; each bare-command entry is wrapped under a
626
+ universal ``"*"`` matcher group.
627
+ * ``[features].codex_hooks`` was aliased as ``[features].hooks``. The
628
+ installer strips any legacy ``codex_hooks`` entry and writes the
629
+ canonical ``hooks = true``.
630
+
631
+ Both migrations are idempotent — re-running install-hook over any
632
+ combination of pre-GA, partially-migrated, or GA configs converges to
633
+ the canonical GA shape with exactly one hyperweave entry.
585
634
  """
586
635
  import json
587
636
 
@@ -590,48 +639,99 @@ def _install_codex_hook(hook_command: str, full_slug: str) -> None:
590
639
  config_path = codex_dir / "config.toml"
591
640
  codex_dir.mkdir(parents=True, exist_ok=True)
592
641
 
593
- # ── Update hooks.json: remove prior hyperweave Stop entries, append new one ──
642
+ # ── Update hooks.json: lift any pre-GA flat keys into the wrapper, then
643
+ # operate on hooks.Stop as the canonical GA structure ──
594
644
  config: dict[str, object] = {}
595
645
  if hooks_path.exists():
596
- config = json.loads(hooks_path.read_text())
597
-
598
- raw_stop = config.setdefault("Stop", [])
599
- stop_hooks: list[object] = raw_stop if isinstance(raw_stop, list) else []
600
- if not isinstance(raw_stop, list):
601
- config["Stop"] = stop_hooks
646
+ loaded = json.loads(hooks_path.read_text())
647
+ if isinstance(loaded, dict):
648
+ config = loaded
649
+
650
+ # GA wrapper: hooks lives under config["hooks"]. Pre-GA configs may not
651
+ # have it yet; create it (or reset if it's the wrong shape).
652
+ wrapper_raw = config.setdefault("hooks", {})
653
+ hooks_wrapper: dict[str, object]
654
+ if isinstance(wrapper_raw, dict):
655
+ hooks_wrapper = wrapper_raw
656
+ else:
657
+ hooks_wrapper = {}
658
+ config["hooks"] = hooks_wrapper
659
+
660
+ # Lift any pre-GA top-level event keys (Stop, PreToolUse, etc.) into the
661
+ # wrapper, wrapping bare-command entries with a universal matcher group.
662
+ # Iterate over a snapshot of keys since we mutate during traversal.
663
+ for legacy_event in list(config.keys()):
664
+ if legacy_event == "hooks" or legacy_event not in _CODEX_HOOK_EVENTS:
665
+ continue
666
+ legacy_value = config.pop(legacy_event)
667
+ if not isinstance(legacy_value, list):
668
+ continue
669
+ target_raw = hooks_wrapper.setdefault(legacy_event, [])
670
+ target: list[object]
671
+ if isinstance(target_raw, list):
672
+ target = target_raw
673
+ else:
674
+ target = []
675
+ hooks_wrapper[legacy_event] = target
676
+ for entry in legacy_value:
677
+ target.append(_wrap_legacy_codex_hook_entry(entry))
678
+
679
+ # Now drop any prior hyperweave matcher groups under hooks.Stop and
680
+ # append the fresh one. A hyperweave group is identified by ANY inner
681
+ # handler whose command mentions "hyperweave session".
682
+ raw_stop = hooks_wrapper.setdefault("Stop", [])
683
+ stop_groups: list[object]
684
+ if isinstance(raw_stop, list):
685
+ stop_groups = raw_stop
686
+ else:
687
+ stop_groups = []
688
+ hooks_wrapper["Stop"] = stop_groups
602
689
 
603
- cleaned = []
604
- for entry in stop_hooks:
605
- if not isinstance(entry, dict):
606
- cleaned.append(entry)
690
+ cleaned: list[object] = []
691
+ for group in stop_groups:
692
+ if not isinstance(group, dict):
693
+ cleaned.append(group)
607
694
  continue
608
- cmd = str(entry.get("command", ""))
609
- if "hyperweave session" in cmd:
610
- continue # drop prior hyperweave hook
611
- cleaned.append(entry)
612
- cleaned.append({"type": "command", "command": hook_command, "timeout": 10})
613
- config["Stop"] = cleaned
695
+ inner_hooks = group.get("hooks", [])
696
+ if isinstance(inner_hooks, list) and any(
697
+ isinstance(h, dict) and "hyperweave session" in str(h.get("command", "")) for h in inner_hooks
698
+ ):
699
+ continue # drop the whole matcher group — owned by hyperweave
700
+ cleaned.append(group)
701
+ cleaned.append(
702
+ {
703
+ "matcher": "*",
704
+ "hooks": [{"type": "command", "command": hook_command, "timeout": 10}],
705
+ }
706
+ )
707
+ hooks_wrapper["Stop"] = cleaned
614
708
  hooks_path.write_text(json.dumps(config, indent=2) + "\n")
615
709
 
616
- # ── Update config.toml: ensure [features] codex_hooks = true (preserve other keys) ──
710
+ # ── Update config.toml: ensure [features] hooks = true (preserve other keys) ──
711
+ # Codex v0.130.0 renamed the gate from `codex_hooks` to `hooks`; strip any
712
+ # legacy key on the way in so re-running install-hook over an older config
713
+ # upgrades cleanly instead of leaving a dead key. Key detection is exact-
714
+ # match (split-on-=, strip), not prefix, so `hooks_*` lookalikes can't
715
+ # spoof a hit.
617
716
  config_lines: list[str] = []
618
717
  if config_path.exists():
619
718
  config_lines = config_path.read_text().splitlines()
719
+ config_lines = [line for line in config_lines if line.split("=", 1)[0].strip() != "codex_hooks"]
620
720
  has_features_section = any(line.strip() == "[features]" for line in config_lines)
621
- has_codex_hooks_key = any(line.strip().startswith("codex_hooks") for line in config_lines)
721
+ has_hooks_key = any("=" in line and line.split("=", 1)[0].strip() == "hooks" for line in config_lines)
622
722
  if not has_features_section:
623
- config_lines.extend(["", "[features]", "codex_hooks = true"])
624
- elif not has_codex_hooks_key:
625
- # Insert codex_hooks = true right after [features] header
723
+ config_lines.extend(["", "[features]", "hooks = true"])
724
+ elif not has_hooks_key:
725
+ # Insert hooks = true right after [features] header
626
726
  for i, line in enumerate(config_lines):
627
727
  if line.strip() == "[features]":
628
- config_lines.insert(i + 1, "codex_hooks = true")
728
+ config_lines.insert(i + 1, "hooks = true")
629
729
  break
630
730
  config_path.write_text("\n".join(config_lines) + "\n")
631
731
 
632
732
  pinned = f" (pinned to {full_slug})" if full_slug else " (auto-detect skin)"
633
733
  typer.echo(f"Installed Stop hook in {hooks_path}{pinned}")
634
- typer.echo(f"Set [features] codex_hooks = true in {config_path}")
734
+ typer.echo(f"Set [features] hooks = true in {config_path}")
635
735
  typer.echo(
636
736
  "Note: Codex Stop fires per-turn — the receipt file refreshes live as the "
637
737
  "session progresses, always reflecting current cumulative state.",
@@ -702,7 +802,7 @@ def install_hook(
702
802
  "auto-detects installed runtimes (~/.claude, ~/.codex, or 'claude'/"
703
803
  "'codex' on PATH) and registers for each present. 'claude-code' "
704
804
  "writes a SessionEnd hook to ~/.claude/settings.json. 'codex' writes "
705
- "a Stop hook to ~/.codex/hooks.json plus [features] codex_hooks in "
805
+ "a Stop hook to ~/.codex/hooks.json plus [features] hooks in "
706
806
  "~/.codex/config.toml. 'all' forces both regardless of detection."
707
807
  ),
708
808
  ),
@@ -771,8 +871,8 @@ def _doctor_runtime_status(runtime: str, home_dir: Path) -> str:
771
871
  Returns ``✓`` when the hyperweave hook is wired correctly, ``✗`` when
772
872
  the runtime is initialized but no hyperweave hook is registered, or
773
873
  ``⚠`` when the wiring is partial (malformed config; codex missing the
774
- [features] codex_hooks flag). The string is rendered verbatim by
775
- ``doctor``.
874
+ [features] hooks flag renamed from ``codex_hooks`` in Codex v0.130.0).
875
+ The string is rendered verbatim by ``doctor``.
776
876
  """
777
877
  import json
778
878
 
@@ -802,24 +902,56 @@ def _doctor_runtime_status(runtime: str, home_dir: Path) -> str:
802
902
  hooks_path = home_dir / "hooks.json"
803
903
  config_path = home_dir / "config.toml"
804
904
  registered_cmd: str | None = None
905
+ legacy_flat_cmd: str | None = None
805
906
  if hooks_path.exists():
806
907
  try:
807
908
  hooks = json.loads(hooks_path.read_text())
808
909
  except json.JSONDecodeError:
809
910
  return f"⚠ {runtime}: ~/.codex/hooks.json is malformed"
810
- for entry in hooks.get("Stop", []) or []:
811
- if not isinstance(entry, dict):
812
- continue
813
- cmd = str(entry.get("command", ""))
814
- if "hyperweave session" in cmd:
815
- registered_cmd = cmd
816
- break
911
+ # GA traversal (Codex v0.129+):
912
+ # hooks["hooks"]["Stop"][group].hooks[handler].command
913
+ wrapper = hooks.get("hooks") if isinstance(hooks, dict) else None
914
+ if isinstance(wrapper, dict):
915
+ for group in wrapper.get("Stop") or []:
916
+ if not isinstance(group, dict):
917
+ continue
918
+ for handler in group.get("hooks") or []:
919
+ if not isinstance(handler, dict):
920
+ continue
921
+ cmd = str(handler.get("command", ""))
922
+ if "hyperweave session" in cmd:
923
+ registered_cmd = cmd
924
+ break
925
+ if registered_cmd:
926
+ break
927
+ # Pre-GA flat fallback (kept for one release): hooks["Stop"][entry]
928
+ # with command directly on the entry. Detected separately so a
929
+ # legacy install surfaces as ⚠ with an upgrade pointer instead of
930
+ # silently misreporting the hook as missing.
931
+ if not registered_cmd and isinstance(hooks, dict):
932
+ for entry in hooks.get("Stop") or []:
933
+ if not isinstance(entry, dict):
934
+ continue
935
+ cmd = str(entry.get("command", ""))
936
+ if "hyperweave session" in cmd:
937
+ legacy_flat_cmd = cmd
938
+ break
939
+ if legacy_flat_cmd and not registered_cmd:
940
+ return (
941
+ f"⚠ {runtime}: hook registered in legacy pre-GA flat format — "
942
+ f"re-run 'hyperweave install-hook --runtime {runtime}' to lift "
943
+ f"it to the GA wrapped structure (Codex v0.129+)"
944
+ )
817
945
  if not registered_cmd:
818
946
  return (
819
947
  f"✗ {runtime}: initialized but no hyperweave hook — run 'hyperweave install-hook --runtime {runtime}'"
820
948
  )
821
- # [features] codex_hooks = true must be present for the hook to fire;
822
- # the install command writes it, but a hand-edited config could miss it.
949
+ # [features] hooks = true must be present for the hook to fire; the
950
+ # install command writes it, but a hand-edited config could miss it.
951
+ # Codex v0.130.0 renamed the gate from `codex_hooks` to `hooks`; key
952
+ # detection is exact-match within the [features] section so a stale
953
+ # `codex_hooks = true` left over from older installs is not mistaken
954
+ # for the live gate (and so `hooks_*` lookalikes can't spoof a hit).
823
955
  feature_ok = False
824
956
  if config_path.exists():
825
957
  in_features = False
@@ -831,12 +963,14 @@ def _doctor_runtime_status(runtime: str, home_dir: Path) -> str:
831
963
  if in_features and stripped.startswith("["):
832
964
  in_features = False
833
965
  continue
834
- if in_features and stripped.startswith("codex_hooks") and "true" in stripped:
835
- feature_ok = True
836
- break
966
+ if in_features and "=" in stripped:
967
+ key, _, value = stripped.partition("=")
968
+ if key.strip() == "hooks" and "true" in value:
969
+ feature_ok = True
970
+ break
837
971
  if not feature_ok:
838
972
  return (
839
- f"⚠ {runtime}: hook registered but [features] codex_hooks = true "
973
+ f"⚠ {runtime}: hook registered but [features] hooks = true "
840
974
  f"is missing — re-run 'hyperweave install-hook --runtime {runtime}'"
841
975
  )
842
976
  return f"✓ {runtime}: hook registered — {registered_cmd}"
@@ -181,7 +181,7 @@ def _base_context(
181
181
  "artifact_id": artifact_id,
182
182
  "contract_id": artifact_id,
183
183
  "frame_type": spec.type,
184
- "genome_id": spec.genome_id,
184
+ "genome_id": resolved.genome.get("id", spec.genome_id),
185
185
  "genome_category": resolved.genome.get("category", "dark"),
186
186
  # v0.3.2: variant + substrate_kind plumbed through to templates.
187
187
  # substrate_kind drives the brutalist split-template dispatcher
@@ -1872,44 +1872,65 @@ def _format_model_label(model: str) -> str:
1872
1872
  return label
1873
1873
 
1874
1874
 
1875
- def _active_window_minutes(stages: list[dict[str, Any]], fallback_m: float) -> float:
1876
- """Active work duration in minutes, bounded by both sum and wall-clock.
1877
-
1878
- Returns ``min(sum_of_stage_durations, wall_clock_first_to_last)``:
1879
- * Sum_of_stages handles idle gaps — sessions left open across multiple
1880
- bursts over days resolve to actual work hours, not wall-clock days.
1881
- * Wall-clock cap handles overlapping/async stages — when stage end
1882
- timestamps extend past the last visible session message (async tool
1883
- completion), the sum can exceed the wall-clock first→last span.
1884
- Without the cap, the chart axis disagrees with the hero subline.
1885
-
1886
- Both are stage-derived, so the chart and hero see the same number from
1887
- the same primary source (stage timestamps) — never from the parser's
1888
- ``duration_minutes`` which can be unreliable.
1875
+ def _active_window_minutes(
1876
+ stages: list[dict[str, Any]],
1877
+ fallback_m: float,
1878
+ turn_duration_m: float | None = None,
1879
+ ) -> float:
1880
+ """Active work duration in minutes.
1881
+
1882
+ Primary source is ``turn_duration_m`` the parser's sum of per-turn
1883
+ compute durations from ``system.turn_duration`` events (Claude Code).
1884
+ This measures agent compute time directly and never absorbs idle gaps
1885
+ that the stage detector failed to break on (a multi-hour break inside
1886
+ a same-class working stretch silently inflates a stage's span).
1887
+
1888
+ Fallback is ``min(sum_of_stage_durations, wall_clock_first_to_last)``
1889
+ when the runtime emits no per-turn duration events (Codex) or when
1890
+ stages lack ISO timestamps (mock fixtures):
1891
+ * Sum_of_stages collapses inter-stage idle in sessions left open
1892
+ across multiple bursts over days.
1893
+ * Wall-clock cap defends against any future stage detector that
1894
+ produces overlapping spans (today's partition algorithm cannot,
1895
+ but the cap costs nothing).
1896
+
1897
+ The wall-clock cap applies to the turn-duration source too — defensive
1898
+ against malformed turn_duration values exceeding the session span.
1889
1899
 
1890
1900
  Returns ``fallback_m`` when stages lack ISO timestamps (mock data) or
1891
1901
  when parsing fails.
1892
1902
  """
1893
1903
  if not stages:
1904
+ if turn_duration_m is not None and turn_duration_m > 0:
1905
+ return max(turn_duration_m, 1.0)
1894
1906
  return fallback_m
1895
- durations: list[float] = []
1896
1907
  starts: list[datetime] = []
1897
1908
  ends: list[datetime] = []
1909
+ durations: list[float] = []
1898
1910
  for s in stages:
1899
1911
  start = s.get("start")
1900
1912
  end = s.get("end")
1901
1913
  if not start or not end:
1914
+ if turn_duration_m is not None and turn_duration_m > 0:
1915
+ return max(turn_duration_m, 1.0)
1902
1916
  return fallback_m
1903
1917
  try:
1904
1918
  t0 = datetime.fromisoformat(start)
1905
1919
  t_end = datetime.fromisoformat(end)
1906
1920
  except (ValueError, TypeError):
1921
+ if turn_duration_m is not None and turn_duration_m > 0:
1922
+ return max(turn_duration_m, 1.0)
1907
1923
  return fallback_m
1908
1924
  starts.append(t0)
1909
1925
  ends.append(t_end)
1910
1926
  durations.append((t_end - t0).total_seconds() / 60.0)
1911
- sum_m = sum(durations)
1912
1927
  wall_clock_m = (max(ends) - min(starts)).total_seconds() / 60.0
1928
+ if turn_duration_m is not None and turn_duration_m > 0:
1929
+ # Cap defensively at wall-clock so a runtime emitting impossibly
1930
+ # large turn_duration values can't make the chart disagree with
1931
+ # itself; in practice turn_duration_m << wall_clock_m always.
1932
+ return max(min(turn_duration_m, wall_clock_m), 1.0)
1933
+ sum_m = sum(durations)
1913
1934
  return max(min(sum_m, wall_clock_m), 1.0)
1914
1935
 
1915
1936
 
@@ -2009,10 +2030,17 @@ def resolve_receipt(
2009
2030
  )
2010
2031
  ]
2011
2032
  duration_m = session.get("duration_minutes", 0)
2012
- # Active window: bounded by both sum-of-stages (collapses idle gaps) and
2013
- # wall-clock span (caps overlapping/async stages). Same value drives the
2033
+ turn_duration_m = session.get("turn_duration_minutes")
2034
+ # Active window: prefers the parser's per-turn compute sum (Claude
2035
+ # Code's `system.turn_duration` events) and falls back to
2036
+ # min(stage-span sum, wall-clock) when the runtime emits no per-turn
2037
+ # signal (Codex) or stages lack timestamps. Same value drives the
2014
2038
  # chart geometry AND the hero subline, so they always agree.
2015
- active_duration_m = _active_window_minutes(stages, float(duration_m))
2039
+ active_duration_m = _active_window_minutes(
2040
+ stages,
2041
+ float(duration_m),
2042
+ float(turn_duration_m) if turn_duration_m is not None else None,
2043
+ )
2016
2044
  wall_clock_m = _wall_clock_minutes(stages, float(duration_m))
2017
2045
  # ``total`` for the divergence flag: max of parser-reported duration and
2018
2046
  # the stage-derived wall-clock. The parser may overstate (idle tail) or
@@ -373,6 +373,10 @@ def parse_transcript(transcript_path: str | Path) -> SessionTelemetry:
373
373
  n_assistant_msgs += 1
374
374
 
375
375
  # ── Compute timestamps + duration ──
376
+ # Codex emits no per-turn duration events analogous to Claude Code's
377
+ # `system.turn_duration`, so `turn_duration_minutes` stays None and the
378
+ # receipt's active-duration line falls back to min(stage-span sum,
379
+ # wall-clock span). Wall-clock is the only signal available here.
376
380
  timestamps = [_parse_timestamp(line.get("timestamp")) for line in raw_lines if line.get("timestamp")]
377
381
  session_start = min(timestamps) if timestamps else datetime.now()
378
382
  session_end = max(timestamps) if timestamps else session_start
@@ -431,6 +435,7 @@ def parse_transcript(transcript_path: str | Path) -> SessionTelemetry:
431
435
  runtime=_REGISTRY.runtime,
432
436
  timestamp=session_start,
433
437
  duration_minutes=round(duration_minutes, 2),
438
+ turn_duration_minutes=None, # Codex has no per-turn duration events
434
439
  tool_calls=all_tool_calls,
435
440
  stages=[], # populated by stages.detect_stages
436
441
  agents=[], # Codex has no Task-style subagent dispatch
@@ -108,6 +108,11 @@ def _assemble(t: SessionTelemetry, cost: float) -> dict[str, Any]:
108
108
  "start": t.timestamp.isoformat(),
109
109
  "end": end,
110
110
  "duration_minutes": t.duration_minutes,
111
+ # Per-turn compute sum when the runtime emits it (Claude Code's
112
+ # `system.turn_duration` events). None for Codex and any runtime
113
+ # without per-turn duration signal; the resolver falls back to
114
+ # min(stage-span sum, wall-clock) in that case.
115
+ "turn_duration_minutes": t.turn_duration_minutes,
111
116
  "model": t.model or "",
112
117
  "git_branch": t.git_branch or "",
113
118
  "project_path": t.project_path or "",
@@ -271,6 +271,18 @@ class SessionTelemetry(BaseModel):
271
271
  )
272
272
  timestamp: datetime = Field(description="Session start time")
273
273
  duration_minutes: float = Field(default=0.0, description="Wall-clock duration")
274
+ turn_duration_minutes: float | None = Field(
275
+ default=None,
276
+ description=(
277
+ "Sum of per-turn compute durations when the runtime emits them. "
278
+ "Claude Code emits `system.turn_duration` events and the parser "
279
+ "sums them here; Codex has no equivalent and leaves this None. "
280
+ "Receipts prefer this over stage-span sums because it counts "
281
+ "agent compute time directly and never absorbs idle gaps that "
282
+ "the stage detector failed to break on. None signals the "
283
+ "fallback path (min of stage-span sum and wall-clock span)."
284
+ ),
285
+ )
274
286
  tool_calls: list[ToolCall] = Field(
275
287
  default_factory=list,
276
288
  description="Flat ordered list of all tool calls",
@@ -368,10 +368,15 @@ def parse_transcript(transcript_path: str | Path) -> SessionTelemetry:
368
368
  session_start = min(timestamps) if timestamps else datetime.now()
369
369
  session_end = max(timestamps) if timestamps else session_start
370
370
 
371
+ # turn_duration_minutes is the receipt's primary "active" source when present —
372
+ # it measures per-turn compute time and ignores idle gaps the stage detector
373
+ # silently absorbs into a stage span. None signals the fallback path.
371
374
  if total_duration_ms > 0:
372
375
  duration_minutes = total_duration_ms / 60_000
376
+ turn_duration_minutes: float | None = duration_minutes
373
377
  else:
374
378
  duration_minutes = (session_end - session_start).total_seconds() / 60
379
+ turn_duration_minutes = None
375
380
 
376
381
  # -- Extract unique file paths (ordered) --
377
382
  files_accessed: list[str] = []
@@ -437,6 +442,7 @@ def parse_transcript(transcript_path: str | Path) -> SessionTelemetry:
437
442
  runtime=_REGISTRY.runtime,
438
443
  timestamp=session_start,
439
444
  duration_minutes=round(duration_minutes, 2),
445
+ turn_duration_minutes=(round(turn_duration_minutes, 2) if turn_duration_minutes is not None else None),
440
446
  tool_calls=all_tool_calls,
441
447
  stages=[],
442
448
  agents=agents,
@@ -45,14 +45,37 @@ def _write_codex_hook(
45
45
  *,
46
46
  with_feature_flag: bool = True,
47
47
  ) -> None:
48
+ """Write a Codex hook in the GA wrapped+matcher format (Codex v0.129+)."""
48
49
  codex_dir = home / ".codex"
49
50
  codex_dir.mkdir(parents=True, exist_ok=True)
50
- hooks = {"Stop": [{"type": "command", "command": command, "timeout": 10}]}
51
+ hooks = {
52
+ "hooks": {
53
+ "Stop": [
54
+ {
55
+ "matcher": "*",
56
+ "hooks": [{"type": "command", "command": command, "timeout": 10}],
57
+ }
58
+ ]
59
+ }
60
+ }
51
61
  (codex_dir / "hooks.json").write_text(json.dumps(hooks, indent=2))
52
- config_lines = ["[features]", "codex_hooks = true"] if with_feature_flag else ["[other]", "key = 1"]
62
+ config_lines = ["[features]", "hooks = true"] if with_feature_flag else ["[other]", "key = 1"]
53
63
  (codex_dir / "config.toml").write_text("\n".join(config_lines) + "\n")
54
64
 
55
65
 
66
+ def _write_codex_hook_legacy_flat(home: Path, command: str) -> None:
67
+ """Write a Codex hook in the pre-GA flat shape (no wrapper, bare entries).
68
+
69
+ Used to drive the doctor's legacy-detection path so a stale install
70
+ surfaces as ⚠ with an upgrade pointer.
71
+ """
72
+ codex_dir = home / ".codex"
73
+ codex_dir.mkdir(parents=True, exist_ok=True)
74
+ hooks = {"Stop": [{"type": "command", "command": command, "timeout": 10}]}
75
+ (codex_dir / "hooks.json").write_text(json.dumps(hooks, indent=2))
76
+ (codex_dir / "config.toml").write_text("[features]\nhooks = true\n")
77
+
78
+
56
79
  # ─────────────────────────────────────────────────────────────────────────────
57
80
  # Doctor — runtime detection states
58
81
  # ─────────────────────────────────────────────────────────────────────────────
@@ -98,8 +121,34 @@ def test_doctor_codex_initialized_but_no_hook(monkeypatch: MonkeyPatch, tmp_path
98
121
  assert "hyperweave install-hook --runtime codex" in result.stdout
99
122
 
100
123
 
124
+ def test_doctor_codex_hook_in_legacy_flat_format_surfaces_warning(monkeypatch: MonkeyPatch, tmp_path: Path) -> None:
125
+ """Pre-GA flat hooks.json surfaces as ⚠ with an upgrade pointer, not ✗.
126
+
127
+ Codex v0.129.0 took hooks GA and changed hooks.json from flat
128
+ ``{Stop: [{type, command, timeout}]}`` to wrapped
129
+ ``{hooks: {Stop: [{matcher, hooks: [{type, command, timeout}]}]}}``.
130
+ A user with a stale install — hyperweave entry written in the flat
131
+ shape — would otherwise look like "no hyperweave hook" to doctor and
132
+ leave the silent symptom (Codex ignores the file, no receipt fires)
133
+ undiagnosed. The legacy-detection branch flips that to ⚠ with a
134
+ re-install pointer.
135
+ """
136
+ _patch_home(monkeypatch, tmp_path)
137
+ _patch_which(monkeypatch, {"claude": None, "codex": None})
138
+ _write_codex_hook_legacy_flat(tmp_path, "hyperweave session receipt --genome telemetry-codex")
139
+ monkeypatch.chdir(tmp_path)
140
+
141
+ runner = CliRunner()
142
+ result = runner.invoke(app, ["doctor"])
143
+ assert result.exit_code == 0
144
+ assert "⚠ codex" in result.stdout
145
+ assert "legacy pre-GA flat format" in result.stdout
146
+ assert "hyperweave install-hook --runtime codex" in result.stdout
147
+
148
+
101
149
  def test_doctor_codex_hook_registered_but_feature_flag_missing(monkeypatch: MonkeyPatch, tmp_path: Path) -> None:
102
- """Codex Stop hook only fires when [features] codex_hooks = true is set."""
150
+ """Codex Stop hook only fires when [features] hooks = true is set (renamed
151
+ from codex_hooks in Codex v0.130.0)."""
103
152
  _patch_home(monkeypatch, tmp_path)
104
153
  _patch_which(monkeypatch, {"claude": None, "codex": None})
105
154
  _write_codex_hook(tmp_path, "hyperweave session receipt", with_feature_flag=False)
@@ -109,7 +158,7 @@ def test_doctor_codex_hook_registered_but_feature_flag_missing(monkeypatch: Monk
109
158
  result = runner.invoke(app, ["doctor"])
110
159
  assert result.exit_code == 0
111
160
  assert "⚠ codex" in result.stdout
112
- assert "codex_hooks" in result.stdout
161
+ assert "[features] hooks = true" in result.stdout
113
162
 
114
163
 
115
164
  def test_doctor_codex_binary_only_state(monkeypatch: MonkeyPatch, tmp_path: Path) -> None: