semiotic 3.8.6 → 3.8.7

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 (213) hide show
  1. package/CLAUDE.md +33 -7
  2. package/README.md +82 -55
  3. package/ai/behaviorContracts.cjs +86 -1
  4. package/ai/componentMetadata.cjs +4 -9
  5. package/ai/dist/mcp-server.js +97 -10
  6. package/ai/examples.md +154 -7
  7. package/ai/schema.json +1611 -145
  8. package/ai/surface-manifest.json +12 -10
  9. package/ai/system-prompt.md +9 -3
  10. package/dist/{chunk-chartContainerDataAudit-FEOD5GTQ.module.min.js → chunk-chartContainerDataAudit-JDSVSKGM.module.min.js} +1 -1
  11. package/dist/chunk-chunk-2YAZHXL4.module.min.js +2 -0
  12. package/dist/{chunk-chunk-G7WFWHAD.module.min.js → chunk-chunk-4ITOQRXM.module.min.js} +1 -1
  13. package/dist/chunk-chunk-5SD6BHVW.module.min.js +2 -0
  14. package/dist/chunk-chunk-6V5VQVCT.module.min.js +2 -0
  15. package/dist/chunk-chunk-7TTBHSAG.module.min.js +2 -0
  16. package/dist/chunk-chunk-7ZDJSYKI.module.min.js +2 -0
  17. package/dist/chunk-chunk-BGHPD3RA.module.min.js +1 -0
  18. package/dist/chunk-chunk-BMRZ2SPU.module.min.js +2 -0
  19. package/dist/chunk-chunk-BNGBEY7N.module.min.js +2 -0
  20. package/dist/chunk-chunk-C7APLVQD.module.min.js +2 -0
  21. package/dist/chunk-chunk-DGWFBOGB.module.min.js +1 -0
  22. package/dist/{chunk-chunk-Q2BS46W2.module.min.js → chunk-chunk-DIVE2DRA.module.min.js} +1 -1
  23. package/dist/chunk-chunk-FBUNUIPV.module.min.js +2 -0
  24. package/dist/chunk-chunk-FQPKOAIH.module.min.js +2 -0
  25. package/dist/chunk-chunk-FRO4TRXK.module.min.js +2 -0
  26. package/dist/chunk-chunk-G7QVPZUD.module.min.js +2 -0
  27. package/dist/chunk-chunk-GBPYV4FP.module.min.js +2 -0
  28. package/dist/chunk-chunk-GU2EJIDJ.module.min.js +2 -0
  29. package/dist/chunk-chunk-J5QD4RAM.module.min.js +2 -0
  30. package/dist/{chunk-chunk-RA7JCG7D.module.min.js → chunk-chunk-JWXLNZSM.module.min.js} +1 -1
  31. package/dist/chunk-chunk-KXAFCCDB.module.min.js +2 -0
  32. package/dist/chunk-chunk-LG7YKJAK.module.min.js +2 -0
  33. package/dist/{chunk-chunk-XP3OHHFX.module.min.js → chunk-chunk-LVHDZAT3.module.min.js} +1 -1
  34. package/dist/chunk-chunk-LWITSR2O.module.min.js +2 -0
  35. package/dist/{chunk-chunk-SMVD3EQJ.module.min.js → chunk-chunk-MX35T6RX.module.min.js} +1 -1
  36. package/dist/chunk-chunk-NH4MEAKP.module.min.js +2 -0
  37. package/dist/chunk-chunk-NJETWX42.module.min.js +2 -0
  38. package/dist/chunk-chunk-NW25WGLS.module.min.js +2 -0
  39. package/dist/{chunk-chunk-GFTVACTO.module.min.js → chunk-chunk-OI7R7EJB.module.min.js} +1 -1
  40. package/dist/{chunk-chunk-OM6NWS4H.module.min.js → chunk-chunk-ONMLEXHR.module.min.js} +1 -1
  41. package/dist/chunk-chunk-OY66GCE6.module.min.js +1 -0
  42. package/dist/chunk-chunk-PZFT457G.module.min.js +1 -0
  43. package/dist/chunk-chunk-Q3WW6JGR.module.min.js +1 -0
  44. package/dist/chunk-chunk-Q6XKQTOQ.module.min.js +1 -0
  45. package/dist/chunk-chunk-RJRM2E6P.module.min.js +1 -0
  46. package/dist/chunk-chunk-RPL7WYXI.module.min.js +1 -0
  47. package/dist/chunk-chunk-SV4XAZ3Q.module.min.js +1 -0
  48. package/dist/chunk-chunk-TSCFL235.module.min.js +1 -0
  49. package/dist/chunk-chunk-UOQRPGPK.module.min.js +2 -0
  50. package/dist/chunk-chunk-V3OAKG32.module.min.js +1 -0
  51. package/dist/chunk-chunk-WLOIUMWG.module.min.js +2 -0
  52. package/dist/chunk-chunk-XEGXX4F7.module.min.js +1 -0
  53. package/dist/chunk-chunk-XFRBLXYM.module.min.js +1 -0
  54. package/dist/chunk-chunk-Z2AJERX5.module.min.js +2 -0
  55. package/dist/chunk-chunk-ZXYDYAB7.module.min.js +2 -0
  56. package/dist/components/ai/SemioticVACPBridge.d.ts +22 -0
  57. package/dist/components/ai/chartCapabilities.d.ts +4 -3
  58. package/dist/components/ai/conversationArc.d.ts +2 -176
  59. package/dist/components/ai/conversationArcTypes.d.ts +202 -0
  60. package/dist/components/ai/readerGrounding.d.ts +12 -0
  61. package/dist/components/ai/vacpAdapter.d.ts +13 -0
  62. package/dist/components/ai/vacpAdapterActions.d.ts +2 -0
  63. package/dist/components/ai/vacpAdapterModel.d.ts +7 -0
  64. package/dist/components/ai/vacpAdapterRuntime.d.ts +26 -0
  65. package/dist/components/ai/vacpAdapterState.d.ts +3 -0
  66. package/dist/components/ai/vacpAdapterTypes.d.ts +169 -0
  67. package/dist/components/ai/vacpTypes.d.ts +160 -0
  68. package/dist/components/charts/index.d.ts +4 -4
  69. package/dist/components/charts/physics/ChainReactionChart.capability.d.ts +9 -0
  70. package/dist/components/charts/physics/ChainReactionChart.d.ts +29 -0
  71. package/dist/components/charts/physics/CollisionSwarmChart.d.ts +1 -1
  72. package/dist/components/charts/physics/EventDropChart.d.ts +7 -2
  73. package/dist/components/charts/physics/GaltonBoardChart.d.ts +1 -1
  74. package/dist/components/charts/physics/{PhysicsPileChart.capability.d.ts → PacketFlowChart.capability.d.ts} +1 -1
  75. package/dist/components/charts/physics/{PhysicalFlowChart.d.ts → PacketFlowChart.d.ts} +22 -6
  76. package/dist/components/charts/physics/PhysicsCustomChart.d.ts +1 -1
  77. package/dist/components/charts/physics/ProcessFlowChart.d.ts +7 -4
  78. package/dist/components/charts/physics/{PhysicalFlowChart.capability.d.ts → UnitPileChart.capability.d.ts} +1 -1
  79. package/dist/components/charts/physics/{PhysicsPileChart.d.ts → UnitPileChart.d.ts} +19 -6
  80. package/dist/components/charts/physics/crucibleChartProps.d.ts +1 -1
  81. package/dist/components/charts/physics/crucibleChartUtils.d.ts +9 -0
  82. package/dist/components/charts/physics/gauntletChartProps.d.ts +6 -1
  83. package/dist/components/charts/physics/gauntletTerminal.d.ts +61 -0
  84. package/dist/components/charts/physics/packetFlowSemantics.d.ts +6 -0
  85. package/dist/components/charts/physics/physicsHocUtils.d.ts +45 -2
  86. package/dist/components/charts/shared/auditAccessibilityStyles.d.ts +9 -0
  87. package/dist/components/charts/shared/knownChartComponents.d.ts +1 -1
  88. package/dist/components/charts/shared/useChartLegend.d.ts +8 -1
  89. package/dist/components/charts/shared/useChartSetup.d.ts +7 -0
  90. package/dist/components/data/portability/capabilityBinding.d.ts +41 -0
  91. package/dist/components/data/portability/index.d.ts +2 -0
  92. package/dist/components/legendLayout.d.ts +56 -0
  93. package/dist/components/recipes/geographicDotGrid.d.ts +82 -0
  94. package/dist/components/recipes/geographicGrid.d.ts +100 -0
  95. package/dist/components/recipes/physics.d.ts +2 -0
  96. package/dist/components/recipes/physicsStageGeography.d.ts +137 -0
  97. package/dist/components/semiotic-ai.d.ts +4 -3
  98. package/dist/components/semiotic-experimental-vacp.d.ts +11 -0
  99. package/dist/components/semiotic-experimental.d.ts +6 -3
  100. package/dist/components/semiotic-geo.d.ts +4 -0
  101. package/dist/components/semiotic-physics.d.ts +9 -6
  102. package/dist/components/semiotic-recipes-core.d.ts +6 -1
  103. package/dist/components/server/renderEvidence.d.ts +1 -1
  104. package/dist/components/server/renderToStaticSVG.d.ts +2 -1
  105. package/dist/components/server/serverChartConfigHeatmap.d.ts +6 -0
  106. package/dist/components/server/serverChartConfigNetworkStyles.d.ts +12 -0
  107. package/dist/components/server/serverChartConfigShared.d.ts +16 -0
  108. package/dist/components/server/serverChartConfigs.d.ts +2 -2
  109. package/dist/components/server/serverChartConfigsPhysics.d.ts +2 -2
  110. package/dist/components/server/serverChartConfigsXY.d.ts +0 -1
  111. package/dist/components/server/staticLegend.d.ts +6 -0
  112. package/dist/components/server/staticSVGChrome.d.ts +10 -2
  113. package/dist/components/server/staticValue.d.ts +8 -0
  114. package/dist/components/stream/geoPipelineConfig.d.ts +10 -0
  115. package/dist/components/stream/legendRenderer.d.ts +8 -0
  116. package/dist/components/stream/physics/PhysicsEvidence.d.ts +37 -0
  117. package/dist/components/stream/physics/PhysicsPipelineStore.d.ts +9 -0
  118. package/dist/components/stream/physics/PhysicsSettledScene.d.ts +5 -0
  119. package/dist/components/stream/physics/physicsFramePipelineConfig.d.ts +19 -0
  120. package/dist/components/stream/physics/physicsPipelineSettle.d.ts +52 -0
  121. package/dist/components/stream/physics/physicsRegionRuntime.d.ts +23 -0
  122. package/dist/components/stream/types.d.ts +2 -0
  123. package/dist/components/types/legendTypes.d.ts +17 -1
  124. package/dist/controls.min.js +1 -1
  125. package/dist/controls.module.min.js +1 -1
  126. package/dist/geo.min.js +1 -1
  127. package/dist/geo.module.min.js +1 -1
  128. package/dist/network.min.js +1 -1
  129. package/dist/network.module.min.js +1 -1
  130. package/dist/ordinal.min.js +1 -1
  131. package/dist/ordinal.module.min.js +1 -1
  132. package/dist/physics.min.js +1 -1
  133. package/dist/physics.module.min.js +1 -1
  134. package/dist/physicsWorker.js +1 -1
  135. package/dist/realtime.min.js +1 -1
  136. package/dist/realtime.module.min.js +1 -1
  137. package/dist/semiotic-ai-core.min.js +1 -1
  138. package/dist/semiotic-ai-core.module.min.js +1 -1
  139. package/dist/semiotic-ai.d.ts +4 -3
  140. package/dist/semiotic-ai.min.js +1 -1
  141. package/dist/semiotic-ai.module.min.js +1 -1
  142. package/dist/semiotic-data.module.min.js +1 -1
  143. package/dist/semiotic-experimental-vacp.d.ts +11 -0
  144. package/dist/semiotic-experimental-vacp.min.js +1 -0
  145. package/dist/semiotic-experimental-vacp.module.min.js +1 -0
  146. package/dist/semiotic-experimental.d.ts +6 -3
  147. package/dist/semiotic-experimental.min.js +2 -1
  148. package/dist/semiotic-experimental.module.min.js +2 -1
  149. package/dist/semiotic-geo.d.ts +4 -0
  150. package/dist/semiotic-physics.d.ts +9 -6
  151. package/dist/semiotic-realtime-core.min.js +1 -1
  152. package/dist/semiotic-realtime-core.module.min.js +1 -1
  153. package/dist/semiotic-recipes-core.d.ts +6 -1
  154. package/dist/semiotic-recipes-core.min.js +1 -1
  155. package/dist/semiotic-recipes-core.module.min.js +1 -1
  156. package/dist/semiotic-recipes.min.js +1 -1
  157. package/dist/semiotic-recipes.module.min.js +1 -1
  158. package/dist/semiotic-server-edge.min.js +1 -1
  159. package/dist/semiotic-server-edge.module.min.js +1 -1
  160. package/dist/semiotic-server-node.min.js +1 -1
  161. package/dist/semiotic-server-node.module.min.js +1 -1
  162. package/dist/semiotic-utils-core.min.js +1 -1
  163. package/dist/semiotic-utils-core.module.min.js +1 -1
  164. package/dist/semiotic-utils.min.js +1 -1
  165. package/dist/semiotic-utils.module.min.js +1 -1
  166. package/dist/semiotic.min.js +1 -1
  167. package/dist/semiotic.module.min.js +1 -1
  168. package/dist/server.min.js +1 -1
  169. package/dist/server.module.min.js +1 -1
  170. package/dist/xy.min.js +1 -1
  171. package/dist/xy.module.min.js +1 -1
  172. package/package.json +18 -2
  173. package/spec/README.md +94 -2
  174. package/spec/bindings/README.md +14 -3
  175. package/spec/v0.1/examples/annotation-provenance-reviewed-revision.json +27 -0
  176. package/spec/v0.1/examples/annotation-provenance-watcher-threshold.json +21 -0
  177. package/spec/v0.1/examples/audience-profile-executive-review.json +24 -0
  178. package/spec/v0.1/examples/audience-profile-incident-operators.json +42 -0
  179. package/spec/v0.1/examples/chart-capability-bar-comparison.json +48 -0
  180. package/spec/v0.1/examples/chart-capability-service-flow.json +54 -0
  181. package/dist/chunk-chunk-2CHN2TA3.module.min.js +0 -1
  182. package/dist/chunk-chunk-3SHB24AG.module.min.js +0 -1
  183. package/dist/chunk-chunk-4MMOOYTX.module.min.js +0 -2
  184. package/dist/chunk-chunk-5RKXZQ2P.module.min.js +0 -1
  185. package/dist/chunk-chunk-75OTQSXS.module.min.js +0 -1
  186. package/dist/chunk-chunk-7ALTGHK6.module.min.js +0 -1
  187. package/dist/chunk-chunk-A5GRQUOF.module.min.js +0 -1
  188. package/dist/chunk-chunk-AAF5LZAU.module.min.js +0 -2
  189. package/dist/chunk-chunk-ARZGA4LJ.module.min.js +0 -2
  190. package/dist/chunk-chunk-C4YA6TDR.module.min.js +0 -1
  191. package/dist/chunk-chunk-CAFLML4L.module.min.js +0 -2
  192. package/dist/chunk-chunk-CO5KQMGH.module.min.js +0 -1
  193. package/dist/chunk-chunk-FHBN7OEB.module.min.js +0 -2
  194. package/dist/chunk-chunk-GMQ5AYTD.module.min.js +0 -1
  195. package/dist/chunk-chunk-IP2UE74X.module.min.js +0 -1
  196. package/dist/chunk-chunk-ITDWLZXV.module.min.js +0 -2
  197. package/dist/chunk-chunk-JCG72SGW.module.min.js +0 -2
  198. package/dist/chunk-chunk-LPSPATPC.module.min.js +0 -1
  199. package/dist/chunk-chunk-PX5YQGDP.module.min.js +0 -2
  200. package/dist/chunk-chunk-QSQVBIJA.module.min.js +0 -1
  201. package/dist/chunk-chunk-RCF52Z42.module.min.js +0 -2
  202. package/dist/chunk-chunk-RNUHEXZA.module.min.js +0 -2
  203. package/dist/chunk-chunk-ROZWKEUG.module.min.js +0 -1
  204. package/dist/chunk-chunk-T2FKJD4O.module.min.js +0 -2
  205. package/dist/chunk-chunk-UDREYOLV.module.min.js +0 -1
  206. package/dist/chunk-chunk-VOWE3SAP.module.min.js +0 -2
  207. package/dist/chunk-chunk-VQPNBUSW.module.min.js +0 -2
  208. package/dist/chunk-chunk-WAWCIHFJ.module.min.js +0 -1
  209. package/dist/chunk-chunk-WYARAXHR.module.min.js +0 -2
  210. package/dist/chunk-chunk-XV3KGYTR.module.min.js +0 -2
  211. package/dist/chunk-chunk-YC2IVEPX.module.min.js +0 -1
  212. package/dist/chunk-chunk-ZOAQDIJO.module.min.js +0 -1
  213. package/dist/chunk-chunk-ZXIXAPRE.module.min.js +0 -2
package/CLAUDE.md CHANGED
@@ -4,16 +4,20 @@
4
4
  - Install: `npm install semiotic`
5
5
  <!-- semiotic-bundle-sizes:start -->
6
6
  <!-- Auto-generated by scripts/sync-bundle-sizes.mjs — do not edit by hand. -->
7
- - **Use sub-path imports** — `semiotic/xy` (143KB gz), `semiotic/ordinal` (119KB gz), `semiotic/network` (125KB gz), `semiotic/geo` (98KB gz), `semiotic/realtime` (152KB gz), `semiotic/realtime/core` (146KB gz), `semiotic/realtime/react` (1KB gz), `semiotic/server` (207KB gz), `semiotic/server/node` (207KB gz), `semiotic/server/edge` (226KB gz), `semiotic/utils` (83KB gz), `semiotic/utils/core` (81KB gz), `semiotic/utils/react` (6KB gz), `semiotic/recipes` (84KB gz), `semiotic/recipes/core` (83KB gz), `semiotic/recipes/react` (2KB gz), `semiotic/themes` (7KB gz), `semiotic/themes/core` (7KB gz), `semiotic/themes/react` (6KB gz), `semiotic/data` (4KB gz), `semiotic/value` (8KB gz), `semiotic/physics` (154KB gz), `semiotic/physics/matter` (1KB gz), `semiotic/physics/rapier` (1KB gz), `semiotic/ai` (468KB gz), `semiotic/ai/core` (78KB gz), `semiotic/controls` (12KB gz), `semiotic/rough` (4KB gz). Full `semiotic` is 382KB gz.
7
+ - **Use sub-path imports** — `semiotic/xy` (145KB gz), `semiotic/ordinal` (120KB gz), `semiotic/network` (126KB gz), `semiotic/geo` (103KB gz), `semiotic/realtime` (153KB gz), `semiotic/realtime/core` (144KB gz), `semiotic/realtime/react` (1KB gz), `semiotic/server` (210KB gz), `semiotic/server/node` (210KB gz), `semiotic/server/edge` (224KB gz), `semiotic/utils` (83KB gz), `semiotic/utils/core` (82KB gz), `semiotic/utils/react` (6KB gz), `semiotic/recipes` (88KB gz), `semiotic/recipes/core` (87KB gz), `semiotic/recipes/react` (2KB gz), `semiotic/themes` (7KB gz), `semiotic/themes/core` (7KB gz), `semiotic/themes/react` (6KB gz), `semiotic/data` (4KB gz), `semiotic/value` (8KB gz), `semiotic/physics` (160KB gz), `semiotic/physics/matter` (1KB gz), `semiotic/physics/rapier` (1KB gz), `semiotic/ai` (486KB gz), `semiotic/ai/core` (80KB gz), `semiotic/controls` (12KB gz), `semiotic/rough` (4KB gz). Full `semiotic` is 389KB gz.
8
8
  <!-- semiotic-bundle-sizes:end -->
9
9
  - CLI: `npx semiotic-ai [--schema|--compact|--examples|--doctor|--audit-a11y]` · MCP: `npx semiotic-mcp`
10
10
 
11
11
  ## Architecture
12
12
  HOC Charts (simple, default) → Stream Frames (full control). Use HOCs unless you need control they don't expose. Stream Frames pass `RealtimeNode`/`RealtimeEdge` wrappers in callbacks, not your data. Every HOC accepts `frameProps`, has an error boundary + dev-mode validation. TypeScript `strict: true`.
13
13
 
14
- ## Common Props (all HOCs)
14
+ ## Common Props (chart HOCs only)
15
15
  `title`, `description` (aria-label), `summary` (sr-only), `width` (600), `height` (400), `responsiveWidth`, `responsiveHeight`, `margin`, `className`, `color` (uniform fill), `stroke`, `strokeWidth`, `opacity`, `enableHover` (true), `tooltip` (boolean | "multi" | function | config), `showLegend`, `showGrid` (false), `frameProps`, `onObservation`, `onClick`, `chartId`, `loading`, `loadingContent` (ReactNode; `false` suppresses), `emptyContent`, `legendInteraction` ("none"|"highlight"|"isolate"), `legendPosition` ("right"|"left"|"top"|"bottom"), `emphasis` ("primary"|"secondary"), `annotations`, `accessibleTable` (true), `hoverHighlight` (requires `colorBy`), `hoverRadius` (30), `animate` (boolean | {duration?, easing?, intro?}), `axisExtent` ("nice"|"exact" — pins first/last tick to data min/max; XY x/y + ordinal value axis only).
16
16
 
17
+ This list is not global. Plain value components do not inherit chart-HOC props and must be
18
+ validated against their own schema. In particular, `BigNumber` uses `label`, `description`, and
19
+ `summary`; it does not accept `title` or `accessibleTable`.
20
+
17
21
  **Primitive styling** (`color`/`stroke`/`strokeWidth`/`opacity`): apply to any shape the chart draws. Precedence: top-level prop > `frameProps.*Style` fn return > HOC base > theme. Use CSS vars (`stroke="var(--semiotic-border)"`) for cascade-overridable theming. Per-datum: use `frameProps.pieceStyle`/`pointStyle`/`lineStyle` fn form.
18
22
 
19
23
  `onClick` receives `(datum, { x, y })`. `onObservation` receives `{ type, datum?, x?, y?, timestamp, chartType, chartId }`.
@@ -55,7 +59,7 @@ HOC Charts (simple, default) → Stream Frames (full control). Use HOCs unless y
55
59
 
56
60
  All ordinal: `colorBy`, `colorScheme`, `categoryFormat` (string|ReactNode), `showCategoryTicks` (true).
57
61
 
58
- **`styleRules`** (declarative threshold-aware styling — **every chart family**) — an ordered `StyleRule[]` where each `{ when, style }` matching a mark contributes its style, merged in list order so **the last applicable rule wins per property** (CSS-cascade model). Wired on: **ordinal** BarChart/StackedBarChart/GroupedBarChart (bars/segments); **XY** LineChart/AreaChart/StackedAreaChart (per-series) + Scatterplot/BubbleChart/QuadrantChart/ConnectedScatterplot (per-point); **network** ForceDirectedGraph/SankeyDiagram/ChordDiagram (nodes); **geo** ChoroplethMap (features) + ProportionalSymbolMap/DistanceCartogram (symbols); **physics** GaltonBoardChart/PhysicsPileChart/CollisionSwarmChart/EventDropChart (particles). `when` = predicate `(datum, ctx) => boolean`, a declarative threshold (`{ axis?: "x"|"y"|"value", field?, gt, gte, lt, lte, eq, ne, within:[min,max], outside, in:[...] }`), or `true`/omitted (always). `ctx` channels by family: bars `{ value, category }`; XY `{ value(=y), x, y }` (use `axis:"x"`/`"y"` to target either axis regardless of accessor field name); network/geo/physics `{ value, category }` (category = the colorBy/group). A rule's `style.fill` may be a color string or a **`HatchFill`** descriptor (`{ type:"hatch", background?, stroke?, spacing?, angle?, lineWidth?, lineOpacity? }`) resolving to a `CanvasPattern` on canvas and an SVG `<pattern>` in SSR — one declaration, both backends. Precedence: top-level primitives > per-mark style fn (`pieceStyle`/`pointStyle`/`nodeStyle`) > `styleRules` > base `colorBy`/`color`/theme. Works through `renderChart`/MCP for all wired families (use the declarative-threshold form, not predicates, when the config must serialize). `HatchFill` is also a `band`/`x-band` annotation `fill` and any scene `style.fill`. Caveats: line/area rules resolve per-series (sample datum, not per-vertex); network hierarchy charts (Tree/Treemap/CirclePack/Orbit) are NOT wired (their `colorByDepth` owns fill); MultiAxisLineChart/MinimapChart/CandlestickChart not wired. Exported from `semiotic` + each family entry + `semiotic/utils`: `StyleRule`, `resolveStyleRules`, `matchesThreshold`, `makeRuleValueResolver`, `makeXYRuleContext`, `makeNodeRuleContext`, `composeStyleRules`, `HatchFill`, `isHatchFill`. See `/features/style-rules`.
62
+ **`styleRules`** (declarative threshold-aware styling — **every chart family**) — an ordered `StyleRule[]` where each `{ when, style }` matching a mark contributes its style, merged in list order so **the last applicable rule wins per property** (CSS-cascade model). Wired on: **ordinal** BarChart/StackedBarChart/GroupedBarChart (bars/segments); **XY** LineChart/AreaChart/StackedAreaChart (per-series) + Scatterplot/BubbleChart/QuadrantChart/ConnectedScatterplot (per-point); **network** ForceDirectedGraph/SankeyDiagram/ChordDiagram (nodes); **geo** ChoroplethMap (features) + ProportionalSymbolMap/DistanceCartogram (symbols); **physics** GaltonBoardChart/UnitPileChart/CollisionSwarmChart/EventDropChart (particles). `when` = predicate `(datum, ctx) => boolean`, a declarative threshold (`{ axis?: "x"|"y"|"value", field?, gt, gte, lt, lte, eq, ne, within:[min,max], outside, in:[...] }`), or `true`/omitted (always). `ctx` channels by family: bars `{ value, category }`; XY `{ value(=y), x, y }` (use `axis:"x"`/`"y"` to target either axis regardless of accessor field name); network/geo/physics `{ value, category }` (category = the colorBy/group). A rule's `style.fill` may be a color string or a **`HatchFill`** descriptor (`{ type:"hatch", background?, stroke?, spacing?, angle?, lineWidth?, lineOpacity? }`) resolving to a `CanvasPattern` on canvas and an SVG `<pattern>` in SSR — one declaration, both backends. Precedence: top-level primitives > per-mark style fn (`pieceStyle`/`pointStyle`/`nodeStyle`) > `styleRules` > base `colorBy`/`color`/theme. Works through `renderChart`/MCP for all wired families (use the declarative-threshold form, not predicates, when the config must serialize). `HatchFill` is also a `band`/`x-band` annotation `fill` and any scene `style.fill`. Caveats: line/area rules resolve per-series (sample datum, not per-vertex); network hierarchy charts (Tree/Treemap/CirclePack/Orbit) are NOT wired (their `colorByDepth` owns fill); MultiAxisLineChart/MinimapChart/CandlestickChart not wired. Exported from `semiotic` + each family entry + `semiotic/utils`: `StyleRule`, `resolveStyleRules`, `matchesThreshold`, `makeRuleValueResolver`, `makeXYRuleContext`, `makeNodeRuleContext`, `composeStyleRules`, `HatchFill`, `isHatchFill`. See `/features/style-rules`.
59
63
 
60
64
  ## Network Charts (`semiotic/network`)
61
65
 
@@ -87,13 +91,17 @@ Process/arrival/distribution charts backed by `StreamPhysicsFrame`. The settled
87
91
 
88
92
  **GaltonBoardChart** — `data`, `valueAccessor` ("value"), `bins` (21), `mode` ("sample"|"mechanical"), `pegRows`, `mechanicalCount`, `branchProbability`, `ballRadius`, `colorBy`, `styleRules` (per-particle; `ctx.category` = colorBy group), `seed`, `size`/`width`/`height`, `paused`, `frameProps`. Renders a deterministic Galton/Plinko-style distribution that settles into bins; mechanical mode can generate a seeded no-data demonstration.
89
93
  **EventDropChart** — `data`, `timeAccessor` ("time"), `arrivalAccessor` ("arrivalTime"), `windows` ({size}), `watermark` ({delay}|fn), `timeScale`, `ballRadius`, `colorBy`, `seed`, `size`, `paused`, `frameProps`. Use for event-time arrival, lateness, and watermark-window stories.
90
- **PhysicsPileChart** — `data`, `categoryAccessor` ("category"), `valueAccessor` ("value"), `mode` ("sample"|"mechanical"), `mechanicalCount`, `mechanicalCategories`, `unitValue` (1), `ballRadius`, `colorBy`, `seed`, `showProjection`, `size`, `paused`, `frameProps`. Unitizes values into repeated bodies that settle into category piles; mechanical mode can generate a seeded no-data capacity sketch; the default projection overlay keeps exact totals readable.
94
+ **UnitPileChart** (was `PhysicsPileChart`; alias kept) — `data`, `categoryAccessor` ("category"), `valueAccessor` ("value"), `mode` ("sample"|"mechanical"), `mechanicalCount`, `mechanicalCategories`, `unitValue` (1), `ballRadius`, `colorBy`, `seed`, `showProjection`, `size`, `paused`, `frameProps`. Unitizes values into repeated bodies that settle into category piles; mechanical mode can generate a seeded no-data capacity sketch; the default projection overlay keeps exact totals readable.
91
95
  **CollisionSwarmChart** — `data`, `xAccessor` ("x"), optional `groupAccessor`, `radiusAccessor`, `pointRadius`, `xExtent`, `collisionIterations`, `settle`, `showProjection`, `colorBy`, `seed`, `size`, `paused`, `frameProps`. Uses springs plus collisions to separate overlapping dots while preserving the quantitative axis and optional group lanes.
92
- **PhysicalFlowChart** — `nodes`, `links`/`edges`/`data`, `nodeIdAccessor` ("id"), `nodeXAccessor` ("x"), `nodeYAccessor` ("y"), `sourceAccessor` ("source"), `targetAccessor` ("target"), `throughputAccessor` ("value"), `pathAccessor` ("path"), `coordinateMode` ("auto"|"normalized"|"pixels"), `particleRate`, `maxParticles`, `particleRadius`, `flowSpeed`, `pathConstraint` ("path"|"none"), `reducedMotion`, `showStaticFlow`, `showNodeLabels`, `showSensors`, `paused`, `seed`, `size`. Experimental physics-backed flow chart where packets move along authored node coordinates or link paths while a static throughput layer keeps route quantities readable.
96
+ **PacketFlowChart** (was `PhysicalFlowChart`; alias kept) — `nodes`, `links`/`edges`/`data`, `nodeIdAccessor` ("id"), `nodeXAccessor` ("x"), `nodeYAccessor` ("y"), `sourceAccessor` ("source"), `targetAccessor` ("target"), `throughputAccessor` ("value"), `pathAccessor` ("path"), `coordinateMode` ("auto"|"normalized"|"pixels"), `particleRate`, `maxParticles`, `particleRadius`, `flowSpeed`, `pathConstraint` ("path"|"none"), `reducedMotion`, `showStaticFlow`, `showNodeLabels`, `showSensors`, `paused`, `seed`, `size`. Experimental physics-backed flow chart where packets move along authored node coordinates or link paths while a static throughput layer keeps route quantities readable.
93
97
  **ProcessFlowChart** — multi-body workflow lane. `data`, `stages` (required: `[{id, label?, force?, damping?, capacity?, pressure?, portal?, absorb?, share?}]`), `stageAccessor` ("stage"), `idAccessor`, `groupBy` (optional feature key; completion when all members hit an absorb stage), `groupLabelAccessor`, `workAccessor`, `radiusAccessor`, `ballRadius` (6), `colorBy`, `groupCompletion` ("allAbsorbed"|"none"), `groupAnchorAlong` (0.55), `showProjection` (true), `showChrome` (true — processChrome kit), `liveCapacity` (true — FIFO queues at `unitsPerSecond`), `onCapacityChange` (queue depth / processed), `bodyLimit` (soft stream budget + oldest eviction), `bodyMark` ("circle"|"halo"|"faceted"|"pill"|"diamond"|"square" or per-row `datum.__physicsMark`), `selection`, `settle`, `seed`, `size`, `paused`, `frameProps`. Settled projection is stage occupancy + capacity badges; use for review queues / triage / merge pipelines. Prefer **GauntletChart** for one compound plan with timed gate effects.
98
+ **Stage geography kit** (`semiotic/physics` + `semiotic/recipes`) — `physicsStageGeography({size, flow ("down"|"right"), destinations (count | [{id,label}]), padding, chargeExtent, destinationExtent, projectionExtent, channelRatio})` → `{charge, apparatus, destinations: [{id,label,order,centerX,centerY,…}], projection}`. The shared **charge → apparatus → destinations** vocabulary every physics chart re-invented locally (Gauntlet `startX`/`socketX`/`graveyardX`, Crucible `chamber`/`mouth`/`outlets`, Galton bins and Pile tubes writing the *same* lane formula). Companions: `physicsStageColliders` (floor + one divider per interior boundary, so bodies land in the bin the data says), `physicsChargePoint` (spread a burst across the entry zone instead of co-locating it), `physicsDestination(geo, id)`, `describePhysicsStageGeography` (one-sentence reading protocol for a11y/agent grounding). Authoring vocabulary for a **new** chart or `PhysicsCustomChart` layout — shipped charts keep their own layouts; a test pins the builder to their lane math.
99
+ **Terminal state (event-tape charts)** — a tape-driven physics chart's end state must be computable from authored inputs with no simulation, because that pure result is what reduced motion, SSR, snapshot export, and `describeChart` receive. `CrucibleChart`: `compileCruciblePlan(...)` → `plan.initialState`/`plan.terminalState`/`plan.terminalSpawns`. `ChainReactionChart`: `initialRuntime(machine, mode, currentTime, true)`. `GauntletChart`: **`resolveGauntletTerminalStates({projects, events, layout, positiveProperties, negativeProperties, viability?, outcome?})`** (also `resolveGauntletTerminalState` for one project) — folds the authored tape, mirroring the live tick's effect order. Caveat: with Gauntlet's `crashDetection` armed (default), physics can still override the outcome to `bad_design_crash`, which no pure fold can predict; the pure result is "what the plan earns on paper". Pass `crashDetection={false}` for a fully authored reading. **Design test for a new physics metaphor:** can you state its ledger in one sentence (else it's an animation), does its terminal state exist without simulating (else it's a movie), would a naive reader guess the reading protocol from the apparatus (else the name is wrong)?
100
+ **Physics family contracts** (every physics HOC) — `seed` (deterministic; an explicit `frameProps.config.kernel.seed` still wins), `rerunMS` (settle → delay → deterministic replay; `null`/omitted = single run), `showProjection`, `paused`, and `selection` + `linkedHover`. `selection` accepts **either** a `{ name }` `SelectionConfig` — joining the shared selection store so physics bodies cross-highlight inside `LinkedCharts` like every other family — **or** a resolved `{ isActive, predicate }` `PhysicsBodySelection` body predicate as the escape hatch. The store's datum predicate is lifted over `body.datum`, so chrome bodies (walls, pegs, tubes) never match a data selection. Under `prefers-reduced-motion` the frame settles to the true end state in one pass: paced arrivals (`initialSpawnPacing`, per-datum `spawnAt`) are all admitted and elapsed-time event tapes run to completion, so the settled projection reports the outcome rather than the authored start.
94
101
  **Physics controllers** (`createCapacityQueueController`, `createPortalController`, `composePhysicsControllers`) — process plugins via `controllers`. Capacity `getSnapshot()` → queueDepth/processedCount; emit `physics-capacity-processed`. **processChrome** (`semiotic/physics` / `semiotic/recipes`) — stage bays, capacity badges, feature sockets (theme: `--semiotic-process-*`). `PhysicsCustomChart`: `layout()` may return `regionEffects`, `controllers`, `bodyForces`; `layoutConfig` hot path without re-enqueue. Guide: `/features/physics-process-guide`. Contracts: `PhysicsContracts.test.tsx`.
95
102
  **GauntletChart** — compound project core + tethered positive/negative property bodies + timed gate events. `positiveProperties`/`negativeProperties` (req), `gates`, `events`, `showChrome` (true), `showProjection` (true — viability/outcome strip), `showTethers` (true), `onStateChange`, `frameProps`. Bodies clamp inside walls (`clampGauntletPoint`). Not for multi-item factory floors (use ProcessFlowChart).
96
103
  **CrucibleChart** — bounded peer components undergo authored `phases` and `events`, form declared `products`, and settle into reason-labelled `outlets` with source lineage. The ledger/projection is authoritative; motion never infers classification, timing, membership, loss, or routing. `buildCrucibleProductEvents({productId, form, contributions?, complete})` is pure authoring sugar for `combine → contribute* → complete-product`; every source/relation id, event position, reason, and outlet remains caller-supplied. The ref handle's `replay()` atomically restarts the deterministic tape even mid-run (`reset()` restores-and-pauses; `rerunMS` repeats after settlement). `playbackRate` changes presentation only. No push/live-event API.
104
+ **ChainReactionChart** — dependency chain reaction. `data`, `taskIDAccessor`, `labelAccessor`, `laneAccessor`, `dependencyAccessor` (all required — edges are never inferred), plus optional `startAccessor`/`endAccessor`/`progressAccessor`/`statusAccessor`/`completionTimeAccessor`/`blockerAccessor`/`milestoneAccessor`. Tasks place by workstream lane × dependency depth; a completed task releases one delivery ball per outgoing edge and a downstream task arms only once every prerequisite ball arrives, so a blocked task's *reach* is the reading. `mode` ("snapshot" derives the settled state at `currentTime` with no simulation | "replay" animates deliveries over that same derived state | "mechanical"), `insight` ("blocker-amplification" reports unfinished downstream tasks + affected lanes), `controls` (play/pause/step/reset/settle), `selectedTaskIDs`/`onSelectionChange`, `reducedMotion: "settle"`, `seed`. Ref handle: `play`/`pause`/`step`/`reset`/`settle`, `previewResolve(taskID)`/`clearPreview` (ask "what would resolving this blocker unlock?" without editing data), `completeTask`/`blockTask`/`unblockTask`, `getAmplification`, `getMachineState`. Task completion is always an explicit data event — the simulation delivers prerequisites, it never decides done. Documented `serverChartConfigs` exclusion: the settled reading is an authored overlay over zero bodies, so the HOC SSR path is the supported static snapshot.
97
105
  **Pop (body-removal burst)** — every physics HOC ref is a `PhysicsFrameHandle` (extends the shared push handle) exposing **`popBodies(ids, options?)`** (`StreamPhysicsPopOptions` = `{ color?, durationMs?, radius? }`): removes the bodies and plays a burst — expanding ring + inner glow + radial sparks fading over `durationMs` (`drawPopAnimations`) — returning the removed ids. It reads as a *departure*, the physics/exit-emphasis counterpart to realtime **`pulse`**'s data-*arrival* glow (the same transient-emphasis metaphor on opposite ends of a datum's life). GauntletChart also fires it internally on gate-driven property removal; `/examples/nimby` (civic-value balloons) and `/examples/merge-pressure` (merge-risk traits) drive it that way.
98
106
 
99
107
  ## Value Charts (`semiotic/value`)
@@ -351,13 +359,31 @@ These rules are generated from `ai/behaviorContracts.cjs` and are consumed by `s
351
359
  Agent action: Use colorBy for categorical encodings. Use CategoryColorProvider or LinkedCharts for cross-chart consistency, colorScheme for per-chart fallback palettes, and avoid frameProps style functions unless intentionally bypassing HOC color resolution.
352
360
  - **Required prop combinations** (`props.required-combinations`): Some chart families need semantic props beyond data. These combinations are enforced by validation/schema for static configs and remain required in push mode unless explicitly noted.
353
361
  Agent action: Before returning code, check the selected component against the required combinations list. For push mode, omit data but keep semantic props such as areaBy, sizeBy, stackBy, and groupBy.
354
- Required combinations: StackedAreaChart: static data + areaBy; push areaBy. Stacked areas need a flat data array plus areaBy to identify the stacked series. BubbleChart: static data + sizeBy; push sizeBy. Bubbles need sizeBy in addition to x/y accessors so radius encodes data rather than a constant point size. StackedBarChart: static data + stackBy; push stackBy. Stacked bars need stackBy to split each category into stack segments. GroupedBarChart: static data + groupBy; push groupBy. Grouped bars need groupBy to split each category into side-by-side bars. SwimlaneChart: static data + subcategoryAccessor; push subcategoryAccessor. Swimlanes need subcategoryAccessor; colorBy defaults to the same field when not provided. GaugeChart: static value; push not supported. GaugeChart is value-only. thresholds, min, max, sweep, and arcWidth are optional. ForceDirectedGraph: static nodes + edges; push nodes + edges. ForceDirectedGraph schema/rendering requires nodes and edges. If an agent infers nodes from edge endpoints, it must materialize a nodes array before returning code.
362
+ Required combinations: StackedAreaChart: static data + areaBy; push areaBy. Stacked areas need a flat data array plus areaBy to identify the stacked series. BubbleChart: static data + sizeBy; push sizeBy. Bubbles need sizeBy in addition to x/y accessors so radius encodes data rather than a constant point size. StackedBarChart: static data + stackBy; push stackBy. Stacked bars need stackBy to split each category into stack segments. GroupedBarChart: static data + groupBy; push groupBy. Grouped bars need groupBy to split each category into side-by-side bars. SwimlaneChart: static data + subcategoryAccessor; push subcategoryAccessor. Swimlanes need subcategoryAccessor; colorBy defaults to the same field when not provided. GaugeChart: static value; push not supported. GaugeChart is value-only. Its thresholds use { value, color, label? }; BigNumber thresholds use the distinct { at, level, color?, label? } vocabulary. ForceDirectedGraph: static nodes + edges; push nodes + edges. ForceDirectedGraph schema/rendering requires nodes and edges. If an agent infers nodes from edge endpoints, it must materialize a nodes array before returning code.
355
363
  - **Push mode omits data** (`streaming.push-mode-data`): HOC push mode is selected by omitting the data prop entirely. Passing data={[]} is static empty data and can clear/reinitialize the frame on render.
356
364
  Agent action: For live charts, create a ref, omit data, then call ref.current.push() or pushMany(). For static renderChart/MCP snapshots, provide data because renderChart cannot push later.
365
+ - **Serialized proposals keep the initial snapshot** (`streaming.serialized-proposal-snapshot`): A JSON component/props proposal for MCP, SSR, or evaluation is a static snapshot even when the request mentions future pushes. Keep the supplied initial rows in the component's real data prop and do not invent pushRows, pushRequirement, ref, or method props.
366
+ Agent action: Return renderable initial props in JSON. If the requested deliverable is React push code, separately omit the controlled data prop in JSX, attach a ref, and call the documented imperative method from an effect or event handler.
367
+ Example: `JSON snapshot: { "component": "LineChart", "props": { "data": [{"week":1,"users":120}], "xAccessor":"week", "yAccessor":"users" } }. React push code instead omits data and calls ref.current?.push(row).`
357
368
  - **Ref mutations need stable IDs** (`streaming.ref-mutations-require-id-accessors`): push() and pushMany() can append without IDs, but remove(id) and update(id, updater) require a stable ID accessor: pointIdAccessor for XY/realtime charts, dataIdAccessor for ordinal charts, and nodeIDAccessor/edgeIdAccessor for network operations.
358
369
  Agent action: When generating code that calls remove() or update(), include the matching ID accessor and make sure pushed rows carry that ID field.
359
370
  - **renderChart uses static props only** (`rendering.renderchart-static-props`): MCP renderChart and semiotic/server renderChart render a single static SVG/PNG snapshot. Browser-only realtime components and future ref pushes are not renderable through that path.
360
371
  Agent action: Use renderChart only with renderable HOC components and complete static data. For live behavior, return React code with a ref and do not promise MCP-rendered output.
372
+ - **Axis formatters are React callbacks** (`serialization.formatters-are-react-callbacks`): xFormat, yFormat, categoryFormat, and valueFormat are callback props, not d3 format strings or axis-title strings. They are intentionally absent from JSON/MCP schemas and string values fail validation.
373
+ Agent action: In serialized props, omit formatter callbacks and use xLabel, yLabel, categoryLabel, or valueLabel for axis titles. In React JSX, pass a function such as xFormat={value => formatAxis(value)}.
374
+ Example: `JSON: { "xLabel": "Payload (KB)", "yLabel": "Response time (ms)" }. React-only: xFormat={value => formatNumber(value, { maximumFractionDigits: 0 })}.`
375
+ - **Value components do not inherit chart-HOC props** (`value.bignumber-wire-contract`): BigNumber is a value component, not a chart HOC, so it does not inherit the common chart-HOC prop list. It uses label as its visible heading and supports description and summary; title and accessibleTable are invalid. Its percent format expects a ratio such as 0.97 and renders it as 97%.
376
+ Agent action: Validate BigNumber against its own prop schema and remove inherited chart-HOC props such as title and accessibleTable before returning a proposal. Use label, description, and summary for text. For a 97-of-100 KPI, either pass value={0.97} with format="percent" or pass value={97} with format="number" and suffix="%"; do not combine value={97} with format="percent".
377
+ Example: `{ "component": "BigNumber", "props": { "value": 97, "label": "SLA attainment", "format": "number", "suffix": "%", "target": { "value": 99, "label": "target", "format": "number" } } }`
378
+ - **Proportional symbol maps use geographic props** (`geo.proportional-symbol-wire-shape`): ProportionalSymbolMap reads point rows from points, longitude from xAccessor (default lon), latitude from yAccessor (default lat), and radius from sizeBy. sizeRange is the two-number pixel-radius range.
379
+ Agent action: Use points, xAccessor, yAccessor, and sizeBy. Do not rename points to data or sizeBy to valueAccessor merely because the map renders circles.
380
+ Example: `{ "points": [{"city":"A","longitude":-122.4,"latitude":37.8,"incidents":18}], "xAccessor":"longitude", "yAccessor":"latitude", "sizeBy":"incidents", "sizeRange":[5,40] }`
381
+ - **Physics charts separate chart mode from simulation input** (`physics.sample-and-mechanical-inputs`): Sample simulations use data plus the chart's accessors. Seeded no-data demonstrations use simulationMode="mechanical" (legacy mode="mechanical" remains accepted); mode otherwise carries chart display modes such as primary or sparkline.
382
+ Agent action: For observed Galton values pass data + valueAccessor. For unit piles pass data + categoryAccessor + valueAccessor. Use seed for reproducibility, bins for Galton columns, and unitValue for the amount represented by one UnitPile body.
383
+ Example: `{ "component": "GaltonBoardChart", "props": { "data": [{"id":"a","value":1}], "valueAccessor":"value", "bins":4, "seed":42 } }`
384
+ - **Physics push methods ingest source records** (`physics.push-uses-source-records`): Physics HOC refs push source records through the chart's accessors. pushRows and dataIdAccessor are not component props; stable source id fields are retained on spawned bodies without an invented accessor.
385
+ Agent action: For React live code, call ref.current?.push(row) or pushMany(rows). For serialized snapshots, append the rows to data. Keep category/value/time accessors, but omit pushRows and dataIdAccessor.
386
+ Example: `ref.current?.push({ id: "c", team: "Blue", value: 2 }); <UnitPileChart ref={ref} categoryAccessor="team" valueAccessor="value" unitValue={1} />`
361
387
 
362
388
  <!-- semiotic-behavior-contracts:end -->
363
389
 
@@ -380,7 +406,7 @@ These rules are generated from `ai/behaviorContracts.cjs` and are consumed by `s
380
406
  - **Tooltip datum shape**: HOC tooltips get raw data. Frame `tooltipContent` gets wrapped — use `d.data`.
381
407
  - **Tooltip format cascade**: `valueFormat`/`xFormat`/`yFormat` flow to default tooltip (axis + tooltip read identically). Custom `tooltip` fully overrides — re-pass via `Tooltip({format})` / `MultiLineTooltip({fields:[{format}]})`. Bespoke-tooltip charts (Histogram, FunnelChart, LikertChart, GaugeChart) don't participate; customize via `tooltip`.
382
408
  - **`tooltip="multi"`**: shows all series at hovered X for LineChart, AreaChart, StackedAreaChart. Custom fn receives `datum.allSeries`.
383
- - **Legend**: "bottom" expands margin ~80px. MultiAxisLineChart: use `legendPosition="bottom"`.
409
+ - **Legend**: "bottom" expands margin ~80px. MultiAxisLineChart: use `legendPosition="bottom"`. A **bottom** legend is placed *outside* the bottom axis chrome (tick labels, axis title) so it doesn't overdraw it; the band is auto-measured. Top axes are opt-in, so a **top** legend reserves nothing by default — set `frameProps.legendLayout.axisGutter` for one. `axisGutter` also overrides the measured bottom band (`0` anchors to the plot edge); `sideGutter` is the left/right counterpart.
384
410
  - **Horizontal bars**: need wider left margin (`margin={{ left: 120 }}`).
385
411
  - **Log scale**: domain min clamped to 1e-6. **`xScaleType: "time"`**: creates `scaleTime`; required for landmark ticks with timestamps.
386
412
  - **`barPadding`**: pixel value (40/60 default). Reduce for small charts.
package/README.md CHANGED
@@ -1,9 +1,10 @@
1
+ [![MCP Toplist](https://mcptoplist.com/badge/io.github.nteract%2Fsemiotic.svg)](https://mcptoplist.com/server/io.github.nteract%2Fsemiotic)
2
+
1
3
  [![Semiotic](semiotic_logo_horizontal.png "semiotic")](https://semiotic.nteract.io)
2
4
 
3
5
  [![CI](https://github.com/nteract/semiotic/actions/workflows/node.js.yml/badge.svg?branch=main)](https://github.com/nteract/semiotic/actions/workflows/node.js.yml)
4
6
  [![npm version](https://img.shields.io/npm/v/semiotic.svg)](https://www.npmjs.com/package/semiotic)
5
7
  [![TypeScript](https://img.shields.io/badge/TypeScript-built--in-blue.svg)](https://www.typescriptlang.org/)
6
- [![semiotic MCP server](https://glama.ai/mcp/servers/nteract/semiotic/badges/card.svg)](https://glama.ai/mcp/servers/nteract/semiotic)
7
8
  [![MseeP.ai Security Assessment Badge](https://mseep.net/pr/nteract-semiotic-badge.png)](https://mseep.ai/app/nteract-semiotic)
8
9
 
9
10
  A React data visualization library designed for AI-assisted development.
@@ -16,21 +17,22 @@ AI coding assistants generate correct chart code on the first try.
16
17
  <img src="./docs/public/assets/img/semiotic-release-dashboard.svg" alt="Semiotic release dashboard showing chart count, bundle sizes, capability coverage, chart families, and documentation growth" width="100%">
17
18
  <!-- semiotic-readme-dashboard:end -->
18
19
 
19
- ## What's New in 3.8.6
20
+ ## What's New in 3.8.7
20
21
 
21
- 3.8.6 adds a ranking chart and semantic-line area bands, then refines legend layout across the
22
- client and server renderers:
22
+ 3.8.7 hardens the physics, server-rendering, and agent-evaluation paths exercised after the 3.8.6
23
+ release:
23
24
 
24
- - New `BumpChart` (`semiotic/xy`): ranks every series within each x-column and connects each
25
- series' rank across columns. Set `ribbon` to encode the original magnitude as true
26
- perpendicular-offset ribbon width instead of a fixed-width line both modes share one
27
- centerline, so the toggle animates as a pure width change.
28
- - `AreaChart` gains `semanticLine` (default on) for hard, value-banded color segments along the
29
- area's top stroke, and `gradientFill`/`lineGradient`/`semanticGradient` now share one
30
- `{ stops }` config (with `GradientConfig` and friends exported).
31
- - Legend layout gains `legendLayout.sideGutter` (space between a side legend and axis chrome) and
32
- a `legendDistance` control, with content-measured side-legend widths and tightened defaults,
33
- aligned across the client and SSR renderers.
25
+ - Physics reduced-motion settling now admits paced spawns and completes time-driven event tapes;
26
+ runtime preference changes no longer accumulate a giant animation delta. Chain-reaction tiles
27
+ and lane labels no longer overlap, and physics chrome follows the active theme.
28
+ - BigNumber now has native static render evidence, exact source facts flow through reader
29
+ grounding, and agent guidance keeps value-component props separate from chart-HOC props.
30
+ - Static SVG preserves chart-level primitive styling and heatmap cell borders, while
31
+ horizontal legends clear axis ticks and titles consistently in browser and server renders.
32
+ - Physics charts participate in shared linked hover, while the clearer `UnitPileChart` and
33
+ `PacketFlowChart` names ship with permanent aliases for their previous names.
34
+ - The public model-evaluation reading room preserves the complete baseline and three targeted
35
+ follow-up trials, including the unresolved Luna gauge result rather than smoothing it away.
34
36
 
35
37
  ```jsx
36
38
  import { LineChart } from "semiotic/xy"
@@ -70,6 +72,19 @@ warnings with typo suggestions, and accessibility features (canvas
70
72
  `<title>`/`<desc>`) so AI-generated code fails gracefully with
71
73
  actionable diagnostics instead of a blank screen.
72
74
 
75
+ ### Accessibility is a release surface
76
+
77
+ The [European Accessibility Act](https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=legissum%3A4403933)
78
+ has applied to covered products and services since 28 June 2025. A chart
79
+ library cannot certify an application's legal compliance: scope, content,
80
+ surrounding controls, testing, and national enforcement remain the
81
+ application owner's responsibility. Semiotic supplies testable infrastructure
82
+ for that work: keyboard interaction, accessible data tables, layered
83
+ descriptions, structured navigation, reduced-motion and forced-colors paths,
84
+ and WCAG-derived contrast tests for shipped theme presets. See the
85
+ [Accessibility docs](https://semiotic.nteract.io/accessibility/overview) and
86
+ run the application's own assistive-technology and user testing.
87
+
73
88
  ### Beyond standard charts
74
89
 
75
90
  **Network visualization.** Force-directed graphs, Sankey diagrams, chord
@@ -83,7 +98,7 @@ semantics. Built-in decay, pulse, and staleness encoding for monitoring dashboar
83
98
 
84
99
  **Coordinated views.** `LinkedCharts` provides hover cross-highlighting,
85
100
  brush cross-filtering, coordinate-based linked crosshairs, and selection
86
- synchronization across any combination of chart types zero wiring.
101
+ synchronization across any combination of chart types through shared selection state.
87
102
 
88
103
  **Geographic visualization.** Choropleth maps, proportional symbol maps, flow
89
104
  maps with animated particles, and distance cartograms — all canvas-rendered
@@ -100,11 +115,11 @@ paths. Opt into placement, hierarchy, density, progressive disclosure,
100
115
  audience-aware amount, provenance, and editorial lifecycle when the chart
101
116
  needs to communicate more than its encoding alone.
102
117
 
103
- ### Start simple, go deep
118
+ ### Choose the API layer
104
119
 
105
120
  | Layer | For | Example |
106
121
  |---|---|---|
107
- | **Charts** | Common visualizations with sensible defaults | `<LineChart data={d} xAccessor="x" yAccessor="y" />` |
122
+ | **Charts** | Common chart forms with chart-level props | `<LineChart data={d} xAccessor="x" yAccessor="y" />` |
108
123
  | **Frames** | Full control over rendering, interaction, and layout | `<StreamXYFrame chartType="line" lineStyle={...} />` |
109
124
 
110
125
  Every Chart component accepts a `frameProps` prop to access the underlying
@@ -181,7 +196,7 @@ Requires React 18.1+ or React 19.
181
196
 
182
197
  ### Coordinated Dashboard
183
198
 
184
- Hover one chart, highlight the same data in another zero wiring:
199
+ Hover one chart and highlight the same data in another through a shared selection:
185
200
 
186
201
  ```jsx
187
202
  import { LinkedCharts, Scatterplot, BarChart } from "semiotic"
@@ -287,7 +302,7 @@ chartRef.current.push({ source: "API", target: "Orders", value: 15 })
287
302
 
288
303
  ### Standard Charts
289
304
 
290
- Line, bar, scatter, area all the basics, with sensible defaults:
305
+ Line, bar, scatter, and area charts share the same accessor-driven API:
291
306
 
292
307
  ```jsx
293
308
  import { LineChart, BarChart } from "semiotic"
@@ -379,35 +394,35 @@ The numbers below are **first-party artifact cost**: the gzip size of Semiotic's
379
394
 
380
395
  | Entry Point | gzip | What's inside |
381
396
  |---|---|---|
382
- | `semiotic/xy` | **143 KB** | LineChart, AreaChart, Scatterplot, Heatmap, + 8 more XY charts |
383
- | `semiotic/ordinal` | **119 KB** | BarChart, PieChart, BoxPlot, Histogram, + 11 more categorical charts |
384
- | `semiotic/network` | **125 KB** | ForceDirectedGraph, SankeyDiagram, ProcessSankey, Treemap, + 4 more |
385
- | `semiotic/geo` | **98 KB** | ChoroplethMap, FlowMap, DistanceCartogram, ProportionalSymbolMap |
386
- | `semiotic/realtime` | **152 KB** | RealtimeLineChart, RealtimeHistogram, + 4 streaming charts |
387
- | `semiotic/realtime/core` | **146 KB** | Streaming chart types, HOCs, and buffer helpers |
397
+ | `semiotic/xy` | **145 KB** | LineChart, AreaChart, Scatterplot, Heatmap, + 8 more XY charts |
398
+ | `semiotic/ordinal` | **120 KB** | BarChart, PieChart, BoxPlot, Histogram, + 11 more categorical charts |
399
+ | `semiotic/network` | **126 KB** | ForceDirectedGraph, SankeyDiagram, ProcessSankey, Treemap, + 4 more |
400
+ | `semiotic/geo` | **103 KB** | ChoroplethMap, FlowMap, DistanceCartogram, ProportionalSymbolMap |
401
+ | `semiotic/realtime` | **153 KB** | RealtimeLineChart, RealtimeHistogram, + 4 streaming charts |
402
+ | `semiotic/realtime/core` | **144 KB** | Streaming chart types, HOCs, and buffer helpers |
388
403
  | `semiotic/realtime/react` | **1 KB** | Stream status and synced push hooks |
389
- | `semiotic/server` | **207 KB** | renderChart, renderDashboard, renderToImage, renderToAnimatedGif |
390
- | `semiotic/server/node` | **207 KB** | renderChart, renderDashboard, renderToImage, renderToAnimatedGif |
391
- | `semiotic/server/edge` | **226 KB** | renderChart, renderChartWithEvidence, renderToStaticSVG, renderDashboard |
404
+ | `semiotic/server` | **210 KB** | renderChart, renderDashboard, renderToImage, renderToAnimatedGif |
405
+ | `semiotic/server/node` | **210 KB** | renderChart, renderDashboard, renderToImage, renderToAnimatedGif |
406
+ | `semiotic/server/edge` | **224 KB** | renderChart, renderChartWithEvidence, renderToStaticSVG, renderDashboard |
392
407
  | `semiotic/utils` | **83 KB** | ThemeProvider, numeric/accessibility audits, serialization — no chart components |
393
- | `semiotic/utils/core` | **81 KB** | Pure theme helpers, numeric/accessibility audits, and serialization |
408
+ | `semiotic/utils/core` | **82 KB** | Pure theme helpers, numeric/accessibility audits, and serialization |
394
409
  | `semiotic/utils/react` | **6 KB** | ThemeProvider, useTheme, useReducedMotion, useHighContrast, useStreamStatus |
395
- | `semiotic/recipes` | **84 KB** | Pure layout functions (waffle, marimekko, flextree, dagre, …) |
396
- | `semiotic/recipes/core` | **83 KB** | Pure layout functions (waffle, marimekko, flextree, dagre, …) |
410
+ | `semiotic/recipes` | **88 KB** | Pure layout functions (waffle, marimekko, flextree, dagre, …) |
411
+ | `semiotic/recipes/core` | **87 KB** | Pure layout functions (waffle, marimekko, flextree, dagre, …) |
397
412
  | `semiotic/recipes/react` | **2 KB** | Glyph and React layout-selection helpers |
398
413
  | `semiotic/themes` | **7 KB** | Theme presets only (tufte, carbon, etc.) |
399
414
  | `semiotic/themes/core` | **7 KB** | Theme presets and token helpers |
400
415
  | `semiotic/themes/react` | **6 KB** | ThemeProvider/useTheme and hooks |
401
416
  | `semiotic/data` | **4 KB** | bin, rollup, groupBy, pivot, fromVegaLite |
402
417
  | `semiotic/value` | **8 KB** | BigNumber — focal-value KPI / scorecard (SingleValueFrame POC) |
403
- | `semiotic/physics` | **154 KB** | GaltonBoardChart, EventDropChart, PhysicsPileChart, CollisionSwarmChart, PhysicalFlowChart, PhysicsCustomChart |
418
+ | `semiotic/physics` | **160 KB** | GaltonBoardChart, EventDropChart, UnitPileChart, CollisionSwarmChart, PacketFlowChart, PhysicsCustomChart |
404
419
  | `semiotic/physics/matter` | **1 KB** | Matter.js migration helpers + optional peer guard (no chart components) |
405
420
  | `semiotic/physics/rapier` | **1 KB** | Rapier peer guard + adapter decision metadata (no chart components) |
406
- | `semiotic/ai` | **468 KB** | All schema-backed charts + validation — optimized for LLM code generation |
407
- | `semiotic/ai/core` | **78 KB** | suggestCharts, auditData, describeChart, repairChartConfig, tool adapters — no chart components |
421
+ | `semiotic/ai` | **486 KB** | All schema-backed charts + validation — optimized for LLM code generation |
422
+ | `semiotic/ai/core` | **80 KB** | suggestCharts, auditData, describeChart, repairChartConfig, tool adapters — no chart components |
408
423
  | `semiotic/controls` | **12 KB** | DirectManipulationControl, CircularBrush, MobileStandardControls, auditVisualizationControls — no frame renderer |
409
424
  | `semiotic/rough` | **4 KB** | Optional deterministic Rough.js paint backend — exact Semiotic geometry remains authoritative |
410
- | `semiotic` | **382 KB** | Everything below (full bundle) |
425
+ | `semiotic` | **389 KB** | Everything below (full bundle) |
411
426
 
412
427
  <!-- semiotic-bundle-sizes:end -->
413
428
 
@@ -428,29 +443,29 @@ Method: fresh `npm pack --ignore-scripts` tarball → temporary consumer → min
428
443
 
429
444
  | Public named import | Runtime | gzip cold-consumer bundle |
430
445
  |---|---:|---:|
431
- | `import { LineChart } from "semiotic"` | browser | **152.8 KiB** |
432
- | `import { LineChart } from "semiotic/xy"` | browser | **152.8 KiB** |
433
- | `import { BarChart } from "semiotic/ordinal"` | browser | **122.9 KiB** |
434
- | `import { SankeyDiagram } from "semiotic/network"` | browser | **127.8 KiB** |
435
- | `import { RealtimeLineChart } from "semiotic/realtime"` | browser | **122.5 KiB** |
436
- | `import { RingBuffer } from "semiotic/realtime/core"` | browser | **170.9 KiB** |
446
+ | `import { LineChart } from "semiotic"` | browser | **153.1 KiB** |
447
+ | `import { LineChart } from "semiotic/xy"` | browser | **153.3 KiB** |
448
+ | `import { BarChart } from "semiotic/ordinal"` | browser | **123.5 KiB** |
449
+ | `import { SankeyDiagram } from "semiotic/network"` | browser | **128.1 KiB** |
450
+ | `import { RealtimeLineChart } from "semiotic/realtime"` | browser | **122.8 KiB** |
451
+ | `import { RingBuffer } from "semiotic/realtime/core"` | browser | **170.6 KiB** |
437
452
  | `import { useStreamStatus } from "semiotic/realtime/react"` | browser | **0.6 KiB** |
438
- | `import { GaltonBoardChart } from "semiotic/physics"` | browser | **138.0 KiB** |
453
+ | `import { GaltonBoardChart } from "semiotic/physics"` | browser | **134.2 KiB** |
439
454
  | `import { MATTER_PHYSICS_CAPABILITIES } from "semiotic/physics/matter"` | browser | **0.2 KiB** |
440
455
  | `import { RAPIER_PHYSICS_CAPABILITIES } from "semiotic/physics/rapier"` | browser | **0.2 KiB** |
441
- | `import { renderChart } from "semiotic/server"` | node | **266.6 KiB** |
442
- | `import { generateFrameSVGs } from "semiotic/server/edge"` | node | **165.0 KiB** |
443
- | `import { renderToImage } from "semiotic/server/node"` | node | **267.0 KiB** |
444
- | `import { suggestCharts } from "semiotic/ai"` | browser | **219.7 KiB** |
445
- | `import { suggestCharts } from "semiotic/ai/core"` | browser | **34.7 KiB** |
456
+ | `import { renderChart } from "semiotic/server"` | node | **269.8 KiB** |
457
+ | `import { generateFrameSVGs } from "semiotic/server/edge"` | node | **164.5 KiB** |
458
+ | `import { renderToImage } from "semiotic/server/node"` | node | **270.2 KiB** |
459
+ | `import { suggestCharts } from "semiotic/ai"` | browser | **193.1 KiB** |
460
+ | `import { suggestCharts } from "semiotic/ai/core"` | browser | **36.3 KiB** |
446
461
  | `import { bin } from "semiotic/data"` | browser | **0.4 KiB** |
447
- | `import { ChoroplethMap } from "semiotic/geo"` | browser | **108.2 KiB** |
462
+ | `import { ChoroplethMap } from "semiotic/geo"` | browser | **108.5 KiB** |
448
463
  | `import { createRoughRenderMode } from "semiotic/rough"` | browser | **3.4 KiB** |
449
464
  | `import { resolveThemePreset } from "semiotic/themes"` | browser | **3.4 KiB** |
450
465
  | `import { resolveThemePreset } from "semiotic/themes/core"` | browser | **3.4 KiB** |
451
466
  | `import { ThemeProvider } from "semiotic/themes/react"` | browser | **4.4 KiB** |
452
- | `import { validateProps } from "semiotic/utils"` | browser | **7.4 KiB** |
453
- | `import { smartTickFormat } from "semiotic/utils/core"` | browser | **0.2 KiB** |
467
+ | `import { validateProps } from "semiotic/utils"` | browser | **8.2 KiB** |
468
+ | `import { smartTickFormat } from "semiotic/utils/core"` | browser | **0.7 KiB** |
454
469
  | `import { useReducedMotion } from "semiotic/utils/react"` | browser | **0.3 KiB** |
455
470
  | `import { waffleLayout } from "semiotic/recipes"` | browser | **2.5 KiB** |
456
471
  | `import { waffleLayout } from "semiotic/recipes/core"` | browser | **2.5 KiB** |
@@ -490,15 +505,23 @@ interface Sale { month: number; revenue: number }
490
505
  ## Server-Side Rendering
491
506
 
492
507
  All chart components render SVG automatically in server environments — no
493
- special imports or configuration needed:
508
+ special imports or configuration needed. Non-streaming chart HOCs can be
509
+ imported and rendered directly from a Next.js Server Component: Semiotic's
510
+ `"use client"` directive defines the package boundary, so the importing page
511
+ does not need its own wrapper or directive. Props crossing that boundary must
512
+ remain serializable; add an app-owned client wrapper only when you introduce
513
+ hooks, callback props, browser state, or a push-driven streaming chart.
494
514
 
495
515
  ```jsx
496
- // Works in Next.js App Router, Remix, Astro — same component, same props
497
- import { LineChart } from "semiotic"
516
+ // app/dashboard/page.tsx a Next.js Server Component
517
+ import { LineChart } from "semiotic/xy"
498
518
 
499
519
  // Server: renders <svg> with path/circle/rect elements
500
520
  // Client: renders <canvas> with SVG overlay for axes
501
- <LineChart data={data} xAccessor="date" yAccessor="value" />
521
+ export default async function DashboardPage() {
522
+ const data = await fetchMetrics()
523
+ return <LineChart data={data} xAccessor="date" yAccessor="value" />
524
+ }
502
525
  ```
503
526
 
504
527
  For standalone SVG/PNG/GIF generation (email, OG images, PDF, Slack), use the server entry point:
@@ -693,7 +716,11 @@ Semiotic is indexed by AI-coding-agent documentation tools so your assistant (Cl
693
716
  - **DeepWiki** — [deepwiki.com/nteract/semiotic](https://deepwiki.com/nteract/semiotic)
694
717
  - **GitMCP** — [gitmcp.io/nteract/semiotic](https://gitmcp.io/nteract/semiotic) (exposes the repo as an MCP endpoint directly)
695
718
  - **Official MCP Registry** — search "semiotic" at [registry.modelcontextprotocol.io](https://registry.modelcontextprotocol.io)
696
- - **Smithery** — [smithery.ai/server/nteract/semiotic](https://smithery.ai/server/nteract/semiotic)
719
+
720
+ The Official MCP Registry is the canonical MCP directory record; it is distinct
721
+ from acceptance into any assistant vendor's curated connector directory.
722
+ Secondary-directory freshness and release ownership are tracked in
723
+ [MCP_DISTRIBUTION.md](https://github.com/nteract/semiotic/blob/main/MCP_DISTRIBUTION.md).
697
724
 
698
725
  Agent-facing API surface:
699
726
 
@@ -102,7 +102,7 @@ const REQUIRED_COMBINATIONS = [
102
102
  required: ["value"],
103
103
  staticRequired: ["value"],
104
104
  pushRequired: [],
105
- summary: "GaugeChart is value-only. thresholds, min, max, sweep, and arcWidth are optional.",
105
+ summary: "GaugeChart is value-only. Its thresholds use { value, color, label? }; BigNumber thresholds use the distinct { at, level, color?, label? } vocabulary.",
106
106
  },
107
107
  {
108
108
  component: "ForceDirectedGraph",
@@ -142,6 +142,15 @@ const PUSH_MODE_COMPONENTS = [
142
142
  "ChordDiagram",
143
143
  "ProportionalSymbolMap",
144
144
  "DistanceCartogram",
145
+ "GaltonBoardChart",
146
+ "EventDropChart",
147
+ "UnitPileChart",
148
+ "CollisionSwarmChart",
149
+ "PacketFlowChart",
150
+ "ProcessFlowChart",
151
+ "GauntletChart",
152
+ "CrucibleChart",
153
+ "ChainReactionChart",
145
154
  ]
146
155
 
147
156
  const STATIC_DATA_COMPONENTS = loadStaticDataComponentsFromSchema()
@@ -201,6 +210,18 @@ const BEHAVIOR_CONTRACTS = [
201
210
  summary: "HOC push mode is selected by omitting the data prop entirely. Passing data={[]} is static empty data and can clear/reinitialize the frame on render.",
202
211
  agentAction: "For live charts, create a ref, omit data, then call ref.current.push() or pushMany(). For static renderChart/MCP snapshots, provide data because renderChart cannot push later.",
203
212
  },
213
+ {
214
+ id: "streaming.serialized-proposal-snapshot",
215
+ category: "streaming",
216
+ title: "Serialized proposals keep the initial snapshot",
217
+ severity: "warning",
218
+ appliesTo: {
219
+ components: PUSH_MODE_COMPONENTS,
220
+ },
221
+ summary: "A JSON component/props proposal for MCP, SSR, or evaluation is a static snapshot even when the request mentions future pushes. Keep the supplied initial rows in the component's real data prop and do not invent pushRows, pushRequirement, ref, or method props.",
222
+ agentAction: "Return renderable initial props in JSON. If the requested deliverable is React push code, separately omit the controlled data prop in JSX, attach a ref, and call the documented imperative method from an effect or event handler.",
223
+ example: 'JSON snapshot: { "component": "LineChart", "props": { "data": [{"week":1,"users":120}], "xAccessor":"week", "yAccessor":"users" } }. React push code instead omits data and calls ref.current?.push(row).',
224
+ },
204
225
  {
205
226
  id: "streaming.ref-mutations-require-id-accessors",
206
227
  category: "streaming",
@@ -221,6 +242,66 @@ const BEHAVIOR_CONTRACTS = [
221
242
  summary: "MCP renderChart and semiotic/server renderChart render a single static SVG/PNG snapshot. Browser-only realtime components and future ref pushes are not renderable through that path.",
222
243
  agentAction: "Use renderChart only with renderable HOC components and complete static data. For live behavior, return React code with a ref and do not promise MCP-rendered output.",
223
244
  },
245
+ {
246
+ id: "serialization.formatters-are-react-callbacks",
247
+ category: "serialization",
248
+ title: "Axis formatters are React callbacks",
249
+ severity: "error",
250
+ appliesTo: {
251
+ propsAny: ["xFormat", "yFormat", "categoryFormat", "valueFormat"],
252
+ },
253
+ summary: "xFormat, yFormat, categoryFormat, and valueFormat are callback props, not d3 format strings or axis-title strings. They are intentionally absent from JSON/MCP schemas and string values fail validation.",
254
+ agentAction: "In serialized props, omit formatter callbacks and use xLabel, yLabel, categoryLabel, or valueLabel for axis titles. In React JSX, pass a function such as xFormat={value => formatAxis(value)}.",
255
+ example: 'JSON: { "xLabel": "Payload (KB)", "yLabel": "Response time (ms)" }. React-only: xFormat={value => formatNumber(value, { maximumFractionDigits: 0 })}.',
256
+ },
257
+ {
258
+ id: "value.bignumber-wire-contract",
259
+ category: "value",
260
+ title: "Value components do not inherit chart-HOC props",
261
+ severity: "error",
262
+ appliesTo: {
263
+ components: ["BigNumber"],
264
+ },
265
+ summary: "BigNumber is a value component, not a chart HOC, so it does not inherit the common chart-HOC prop list. It uses label as its visible heading and supports description and summary; title and accessibleTable are invalid. Its percent format expects a ratio such as 0.97 and renders it as 97%.",
266
+ agentAction: "Validate BigNumber against its own prop schema and remove inherited chart-HOC props such as title and accessibleTable before returning a proposal. Use label, description, and summary for text. For a 97-of-100 KPI, either pass value={0.97} with format=\"percent\" or pass value={97} with format=\"number\" and suffix=\"%\"; do not combine value={97} with format=\"percent\".",
267
+ example: '{ "component": "BigNumber", "props": { "value": 97, "label": "SLA attainment", "format": "number", "suffix": "%", "target": { "value": 99, "label": "target", "format": "number" } } }',
268
+ },
269
+ {
270
+ id: "geo.proportional-symbol-wire-shape",
271
+ category: "geo",
272
+ title: "Proportional symbol maps use geographic props",
273
+ severity: "error",
274
+ appliesTo: {
275
+ components: ["ProportionalSymbolMap"],
276
+ },
277
+ summary: "ProportionalSymbolMap reads point rows from points, longitude from xAccessor (default lon), latitude from yAccessor (default lat), and radius from sizeBy. sizeRange is the two-number pixel-radius range.",
278
+ agentAction: "Use points, xAccessor, yAccessor, and sizeBy. Do not rename points to data or sizeBy to valueAccessor merely because the map renders circles.",
279
+ example: '{ "points": [{"city":"A","longitude":-122.4,"latitude":37.8,"incidents":18}], "xAccessor":"longitude", "yAccessor":"latitude", "sizeBy":"incidents", "sizeRange":[5,40] }',
280
+ },
281
+ {
282
+ id: "physics.sample-and-mechanical-inputs",
283
+ category: "physics",
284
+ title: "Physics charts separate chart mode from simulation input",
285
+ severity: "warning",
286
+ appliesTo: {
287
+ components: ["GaltonBoardChart", "UnitPileChart", "CollisionSwarmChart"],
288
+ },
289
+ summary: "Sample simulations use data plus the chart's accessors. Seeded no-data demonstrations use simulationMode=\"mechanical\" (legacy mode=\"mechanical\" remains accepted); mode otherwise carries chart display modes such as primary or sparkline.",
290
+ agentAction: "For observed Galton values pass data + valueAccessor. For unit piles pass data + categoryAccessor + valueAccessor. Use seed for reproducibility, bins for Galton columns, and unitValue for the amount represented by one UnitPile body.",
291
+ example: '{ "component": "GaltonBoardChart", "props": { "data": [{"id":"a","value":1}], "valueAccessor":"value", "bins":4, "seed":42 } }',
292
+ },
293
+ {
294
+ id: "physics.push-uses-source-records",
295
+ category: "physics",
296
+ title: "Physics push methods ingest source records",
297
+ severity: "error",
298
+ appliesTo: {
299
+ components: ["GaltonBoardChart", "EventDropChart", "UnitPileChart", "CollisionSwarmChart"],
300
+ },
301
+ summary: "Physics HOC refs push source records through the chart's accessors. pushRows and dataIdAccessor are not component props; stable source id fields are retained on spawned bodies without an invented accessor.",
302
+ agentAction: "For React live code, call ref.current?.push(row) or pushMany(rows). For serialized snapshots, append the rows to data. Keep category/value/time accessors, but omit pushRows and dataIdAccessor.",
303
+ example: 'ref.current?.push({ id: "c", team: "Blue", value: 2 }); <UnitPileChart ref={ref} categoryAccessor="team" valueAccessor="value" unitValue={1} />',
304
+ },
224
305
  ]
225
306
 
226
307
  function hasOwn(value, key) {
@@ -284,6 +365,7 @@ function formatDoctorBehaviorContracts(contracts) {
284
365
  lines.push(` ${formatRequiredCombination(combo)}`)
285
366
  }
286
367
  }
368
+ if (contract.example) lines.push(` Example: ${contract.example}`)
287
369
  lines.push(` Action: ${contract.agentAction}`)
288
370
  }
289
371
  return lines.join("\n")
@@ -308,6 +390,9 @@ function formatBehaviorContractsMarkdown({ compact = false } = {}) {
308
390
  const combos = contract.combinations.map(formatRequiredCombination).join(" ")
309
391
  lines.push(` Required combinations: ${combos}`)
310
392
  }
393
+ if (!compact && contract.example) {
394
+ lines.push(` Example: \`${contract.example}\``)
395
+ }
311
396
  }
312
397
 
313
398
  lines.push("", DOC_MARKER_END)
@@ -25,8 +25,9 @@ const COMPONENTS_BY_CATEGORY = {
25
25
  "RealtimeWaterfallChart", "RealtimeHeatmap",
26
26
  ],
27
27
  physics: [
28
- "GaltonBoardChart", "EventDropChart", "PhysicsPileChart", "CollisionSwarmChart",
29
- "PhysicalFlowChart", "ProcessFlowChart", "GauntletChart", "CrucibleChart",
28
+ "GaltonBoardChart", "EventDropChart", "UnitPileChart", "CollisionSwarmChart",
29
+ "PacketFlowChart", "ProcessFlowChart", "GauntletChart", "CrucibleChart",
30
+ "ChainReactionChart",
30
31
  ],
31
32
  value: [
32
33
  "BigNumber",
@@ -67,17 +68,11 @@ function metadataForComponent(entryOrName) {
67
68
  // any other static HOC. Matches the name-prefix exclusion the
68
69
  // check-surface-parity script applies.
69
70
  const isPushOnly = category === "realtime" && name.startsWith("Realtime")
70
- // Value-family charts (BigNumber, future SingleValueFrame HOCs)
71
- // render via react-dom/server in a normal React tree, but they do
72
- // not route through the frame-driven `renderChart` path in
73
- // serverChartConfigs.ts — so they are not MCP-renderable. Mirrors
74
- // the `hoc-ssr-only` special feature documented in chartSpecs.ts.
75
- const isValueCategory = category === "value"
76
71
  return {
77
72
  name,
78
73
  category,
79
74
  importPath: importPathForCategory(category),
80
- renderable: !isPushOnly && !isValueCategory,
75
+ renderable: !isPushOnly,
81
76
  description: typeof entryOrName === "string" ? undefined : entryOrName.description,
82
77
  }
83
78
  }