Loading the parity record
Fetching the measured run — the pinned upstream oracle, every symbol and every case.
Fetching the measured run — the pinned upstream oracle, every symbol and every case.
Parity / Streamlit
Every number on this page was produced by running both implementations over the same cases: the real streamlit package pinned at 1.61.1 answers first, and its answer is the expectation the Go port is held to. Nothing is a hand-written expectation, so a new upstream release re-scores the port on its own. See Streamlit for the port's own documentation. Source: github.com/malcolmston/streamlit.
Not a generic diagram: every node below names an artefact of this harness — the pinned package it installed, the runner files it started, the case files it streamed, and the counts it wrote out.
| Case group | Cases | Match | Mismatch | Group parity |
|---|---|---|---|---|
| widget-values | 17 | 12 | 5 | 70.6% |
| widgets | 16 | 13 | 3 | 81.3% |
| charts | 4 | 0 | 4 | 0.0% |
| tabular | 4 | 4 | 0 | 100.0% |
| layout | 3 | 3 | 0 | 100.0% |
| caching | 2 | 2 | 0 | 100.0% |
| control | 2 | 1 | 1 | 50.0% |
| forms | 2 | 2 | 0 | 100.0% |
| state | 2 | 2 | 0 | 100.0% |
| text | 2 | 1 | 1 | 50.0% |
| keys | 1 | 0 | 1 | 0.0% |
Every exported symbol of the upstream package, and what the port offers for it. The upstream list is derived mechanically, never from a README: There is no exported `Run(app, event) *Element`, no exported session constructor, and no testing package, so every consumer that wants to test an app must re-derive the protocol from the source.. A symbol with no case is untested, never a match.
| Upstream symbol | Go symbol | Status | Cases | Note |
|---|---|---|---|---|
| st.App | — | missing | — | multipage apps not ported |
| st.Page | — | missing | — | multipage apps not ported |
| st.altair_chart | — | missing | — | no Vega/Altair spec support |
| st.area_chart | st.Container.AreaChart | differs | chart-area | |
| st.audio | st.Container.Audio | untested | — | |
| st.audio_input | st.Container.AudioInput | untested | — | bytes arrive over multipart /api/upload |
| st.badge | st.Container.Badge | untested | — | |
| st.balloons | st.Container.Balloons | untested | — | |
| st.bar_chart | st.Container.BarChart | differs | chart-bar | |
| st.button | st.Container.Button | differs | wd-button, widgets-defaults-all, button-value-prop, button-one-run, session-state-counter | element carries no clicked state |
| st.cache | — | missing | — | removed upstream alias; nothing to port |
| st.cache_data | st.Session.Cache | match | cache-hit-then-miss, cache-invalidate | key must be derived by the caller; no arg hashing, no per-function .clear() |
| st.cache_resource | — | missing | — | Session.CacheResource exists only in the unreleased tree, not in v0.3.0 |
| st.camera_input | st.Container.CameraInput | untested | — | |
| st.caption | st.Container.Caption | match | text-elements | |
| st.chat_input | st.Container.ChatInput | untested | — | |
| st.chat_message | st.Container.ChatMessage | untested | — | |
| st.checkbox | st.Container.Checkbox | differs | wd-checkbox, set-checkbox, widgets-presentation | no help/disabled |
| st.code | st.Container.Code | match | text-elements | |
| st.color_picker | st.Container.ColorPicker | untested | — | |
| st.columns | st.Container.Columns | match | layout-basic, layout-nested, layout-widget-in-column | ColumnsWeighted (relative widths) exists only in the unreleased tree |
| st.connection | — | missing | — | no data-source connections |
| st.container | st.Container.Container | match | layout-basic | border=True only in the unreleased tree |
| st.data_editor | — | missing | — | no editable grid |
| st.dataframe | st.Container.DataFrame | match | dataframe | takes [][]string / []struct, not a DataFrame; cells are pre-stringified |
| st.date_input | st.Container.DateInput | match | wd-date_input, set-date-input | ISO strings, no min/max, no range form at this tag |
| st.datetime_input | — | missing | — | |
| st.dialog | — | missing | — | no modal dialogs |
| st.divider | st.Container.Divider | match | text-elements | |
| st.download_button | st.Container.DownloadButton | untested | — | |
| st.echo | st.Container.Echo | untested | — | takes the source as a string; cannot read its own source |
| st.empty | st.Container.Empty | untested | — | |
| st.error | st.Container.Error | match | text-elements | |
| st.exception | st.Container.Exception | untested | — | |
| st.expander | st.Container.Expander | match | layout-basic, layout-nested | |
| st.feedback | st.Container.Feedback | untested | — | |
| st.file_uploader | st.Container.FileUploader | match | wd-file_uploader, widgets-defaults-all | uploads only via multipart POST /api/upload; not driven by a case |
| st.form | st.Container.Form | match | form-atomic-commit, form-submit-without-changes | |
| st.form_submit_button | st.Container.FormSubmitButton | match | form-atomic-commit, form-submit-without-changes | reuses the form key as its widget key |
| st.fragment | — | missing | — | no partial reruns |
| st.get_option | — | missing | — | no config system |
| st.graphviz_chart | — | missing | — | |
| st.header | st.Container.Header | match | text-elements | |
| st.help | st.Container.Help | untested | — | |
| st.html | st.Container.Html | untested | — | |
| st.iframe | — | missing | — | |
| st.image | st.Container.Image | untested | — | |
| st.info | st.Container.Info | match | text-elements | |
| st.json | st.Container.JSON | match | json-element | |
| st.latex | st.Container.Latex | match | text-elements | |
| st.line_chart | st.Container.LineChart | differs | chart-line | |
| st.link_button | st.Container.LinkButton | untested | — | |
| st.login | — | missing | — | no OIDC auth |
| st.logo | st.Container.Logo | untested | — | |
| st.logout | — | missing | — | no OIDC auth |
| st.map | st.Container.Map | untested | — | |
| st.markdown | st.Container.Markdown | differs | text-elements, write-dispatch | unsafe_allow_html only in the unreleased tree |
| st.menu_button | — | missing | — | |
| st.mermaid_chart | — | missing | — | |
| st.metric | st.Container.Metric | differs | metric | element carries no derived direction/color at this tag |
| st.multiselect | st.Container.MultiSelect | match | wd-multiselect, set-multiselect, multiselect-unknown-value | no default= parameter: always starts empty |
| st.navigation | — | missing | — | multipage apps not ported |
| st.number_input | st.Container.NumberInput | differs | wd-number_input, set-number-input, number-above-max, number-below-min, number-input-step | no step; bounds only via the Go-only NumberInputRange |
| st.page_link | st.Container.PageLink | untested | — | |
| st.pagination | — | missing | — | |
| st.pdf | — | missing | — | |
| st.pills | st.Container.Pills | untested | — | |
| st.plotly_chart | — | missing | — | |
| st.popover | st.Container.Popover | untested | — | |
| st.progress | st.Container.Progress | untested | — | |
| st.pydeck_chart | — | missing | — | |
| st.pyplot | — | missing | — | |
| st.radio | st.Container.Radio | match | wd-radio, set-radio | no index=/default parameter: always the first option |
| st.rerun | — | missing | — | Session.Rerun exists only in the unreleased tree, not in v0.3.0 |
| st.scatter_chart | st.Container.ScatterChart | differs | chart-scatter | |
| st.segmented_control | st.Container.SegmentedControl | untested | — | |
| st.select_slider | st.Container.SelectSlider | untested | — | |
| st.selectbox | st.Container.SelectBox | differs | wd-selectbox, set-selectbox, selectbox-unknown-value | accepts an off-list value from the client where upstream cannot express one |
| st.set_option | — | missing | — | no config system |
| st.set_page_config | st.Session.SetPageConfig | untested | — | title and icon only |
| st.skeleton | — | missing | — | |
| st.slider | st.Container.Slider | differs | wd-slider, set-slider, slider-above-max, slider-below-min | clamps an out-of-range persisted value; upstream reverts to the default |
| st.snow | st.Container.Snow | untested | — | |
| st.space | — | missing | — | |
| st.spinner | st.Container.Spinner | untested | — | decorative only: the tree ships after the run finishes |
| st.status | st.Container.Status | untested | — | |
| st.stop | st.Session.Stop | differs | stop-halts-run, stop-swallowed-by-app | panics an unexported sentinel a recover() in app code swallows |
| st.subheader | st.Container.Subheader | match | text-elements | |
| st.success | st.Container.Success | match | text-elements | |
| st.switch_page | — | missing | — | multipage apps not ported |
| st.table | st.Container.Table | match | table | |
| st.tabs | st.Container.Tabs | match | layout-basic | |
| st.text | st.Container.Text | match | text-elements, layout-basic | |
| st.text_area | st.Container.TextArea | match | wd-text_area, set-text-area | no max_chars/placeholder/height |
| st.text_input | st.Container.TextInput | differs | wd-text_input, set-text-input, layout-widget-in-column, widgets-presentation | no help/disabled/placeholder; max_chars only via the Go-only TextInputMax |
| st.time_input | st.Container.TimeInput | match | wd-time_input, set-time-input | "15:04" strings, no step |
| st.title | st.Container.Title | match | text-elements | |
| st.toast | st.Container.Toast | untested | — | |
| st.toggle | st.Container.Toggle | untested | — | |
| st.vega_lite_chart | — | missing | — | charts are server-rendered SVG, not a Vega-Lite spec |
Every case the harness streamed to both runners, with the exact upstream symbol and Go symbol it exercised. A deliberate, documented difference is a deviation and is counted apart from a mismatch.
| Case | Group | Upstream symbol | Go symbol | Status | Note |
|---|---|---|---|---|---|
| cache-hit-then-miss | caching | st.cache_data | st.Session.Cache | match | recomputes only when the key/arguments change |
| cache-invalidate | caching | st.cache_data.clear | st.CacheClear | match | explicit invalidation forces exactly one recomputation |
| chart-line | charts | st.line_chart | st.Container.LineChart | mismatch | mark kind and encoded series |
| chart-area | charts | st.area_chart | st.Container.AreaChart | mismatch | |
| chart-bar | charts | st.bar_chart | st.Container.BarChart | mismatch | |
| chart-scatter | charts | st.scatter_chart | st.Container.ScatterChart | mismatch | |
| stop-halts-run | control | st.stop | st.Session.Stop | match | |
| stop-swallowed-by-app | control | st.stop | st.Session.Stop | mismatch | st.stop requests a stop from the script runner and cannot be caught; Session.Stop panics an unexported sentinel that a recover() in app code swallows |
| form-atomic-commit | forms | st.form + st.form_submit_button | st.Container.Form + FormSubmitButton | match | staged values stay invisible until submit, then commit together; the submit button reads true for exactly one run |
| form-submit-without-changes | forms | st.form_submit_button | st.Container.FormSubmitButton | match | |
| auto-key-shift | keys | st.slider (no key) | st.Container.Slider (no key) | mismatch | revealing a conditional widget must not reassign a later widget's persisted value |
| layout-basic | layout | st.columns/st.tabs/st.expander/st.container/st.sidebar | st.Container.Columns/Tabs/Expander/Container + st.Session.Sidebar | match | |
| layout-nested | layout | st.columns + st.expander | st.Container.Columns + Expander | match | |
| layout-widget-in-column | layout | st.columns + st.text_input | st.Container.Columns + TextInput | match | a widget nested in a layout container still round-trips its value |
| session-state-counter | state | st.session_state | st.Session.State | match | state survives reruns and a button only bumps the counter once per click |
| button-one-run | state | st.button | st.Container.Button | match | a button must read true for exactly one run |
| dataframe | tabular | st.dataframe | st.Container.DataFrame | match | |
| table | tabular | st.table | st.Container.Table | match | |
| json-element | tabular | st.json | st.Container.JSON | match | |
| metric | tabular | st.metric | st.Container.Metric | match | label, value, delta and the derived arrow direction and colour |
| text-elements | text | st.title/header/subheader/markdown/caption/latex/code/text/divider/success/info/warning/error | st.Container.Title/Header/Subheader/Markdown/Caption/Latex/Code/Text/Divider/Success/Info/Warning/Error | match | every text element and all four alert kinds, in document order |
| write-dispatch | text | st.write | st.Container.Write | mismatch | st.write's type dispatch for string, int and bool |
| set-checkbox | widget-values | st.checkbox | st.Container.Checkbox | match | |
| set-radio | widget-values | st.radio | st.Container.Radio | match | |
| set-selectbox | widget-values | st.selectbox | st.Container.SelectBox | match | |
| set-multiselect | widget-values | st.multiselect | st.Container.MultiSelect | match | |
| set-slider | widget-values | st.slider | st.Container.Slider | match | |
| set-text-input | widget-values | st.text_input | st.Container.TextInput | match | |
| set-text-area | widget-values | st.text_area | st.Container.TextArea | match | |
| set-number-input | widget-values | st.number_input | st.Container.NumberInput | match | |
| set-date-input | widget-values | st.date_input | st.Container.DateInput | match | |
| set-time-input | widget-values | st.time_input | st.Container.TimeInput | match | |
| slider-above-max | widget-values | st.slider | st.Container.Slider | mismatch | value clamping: what happens to a persisted value outside [min,max] |
| slider-below-min | widget-values | st.slider | st.Container.Slider | mismatch | |
| number-above-max | widget-values | st.number_input | st.Container.NumberInputRange | mismatch | |
| number-below-min | widget-values | st.number_input | st.Container.NumberInputRange | mismatch | |
| selectbox-unknown-value | widget-values | st.selectbox | st.Container.SelectBox | mismatch | a persisted selection that is no longer an option |
| multiselect-unknown-value | widget-values | st.multiselect | st.Container.MultiSelect | match | |
| widget-hidden-then-shown | widget-values | st.text_input | st.Container.TextInput | match | does a widget hidden by a branch keep its value when it comes back |
| wd-button | widgets | st.button | st.Container.Button | match | default element + observed value |
| wd-checkbox | widgets | st.checkbox | st.Container.Checkbox | match | |
| wd-radio | widgets | st.radio | st.Container.Radio | match | |
| wd-selectbox | widgets | st.selectbox | st.Container.SelectBox | match | |
| wd-multiselect | widgets | st.multiselect | st.Container.MultiSelect | match | |
| wd-slider | widgets | st.slider | st.Container.Slider | match | |
| wd-text_input | widgets | st.text_input | st.Container.TextInput | match | |
| wd-text_area | widgets | st.text_area | st.Container.TextArea | match | |
| wd-number_input | widgets | st.number_input | st.Container.NumberInput | match | |
| wd-date_input | widgets | st.date_input | st.Container.DateInput | match | |
| wd-time_input | widgets | st.time_input | st.Container.TimeInput | match | |
| wd-file_uploader | widgets | st.file_uploader | st.Container.FileUploader | match | |
| widgets-defaults-all | widgets | st.button…st.file_uploader | st.Container.Button…FileUploader | match | the whole widget set in one tree, checking document order too |
| widgets-presentation | widgets | st.checkbox(help=,disabled=)/st.slider(format=)/st.text_input(max_chars=,placeholder=) | st.Container.Checkbox/Slider/TextInputMax | mismatch | opt-in extras: format, help, disabled, placeholder, maxChars |
| button-value-prop | widgets | st.button | st.Container.Button | mismatch | full profile: does the button element itself carry its clicked state |
| number-input-step | widgets | st.number_input(step=) | st.Container.NumberInput | mismatch | full profile: does the number input element carry a step |
A case can show the port is less safe than the library it ports. Those findings are written into security.json and filed as draft advisories, never as public issues with a working repro.
| Finding | Severity | Affected | Cases | Summary |
|---|---|---|---|---|
| GHSA-h2fv-7q2m-px4x | medium | <= 0.3.0 | — | Unauthenticated remote memory exhaustion: unbounded widget-state, session table, and request bodies from client-controlled input |