Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
44415ba
Add Matomo analytics integration with GDPR consent support (#341)
t0mdavid-m Feb 20, 2026
c65d503
Remove duplicate `address` key in `.streamlit/config.toml` (#346)
Copilot Mar 3, 2026
c81fee6
Fix integration test failures caused by sys.modules pollution and shu…
Copilot Mar 11, 2026
fb2fe67
Remove server address from bundled config.toml for Windows installer …
t0mdavid-m Mar 14, 2026
128da6d
reenable cross origin protection
t0mdavid-m Mar 14, 2026
42fc187
Add CLAUDE.md and Claude Code skills for MS webapp development (#357)
t0mdavid-m Mar 27, 2026
6a2bc03
Add Kubernetes manifests and CI/CD workflows for deployment (#347)
t0mdavid-m Apr 2, 2026
93170fa
Claude/kubernetes migration plan kq jw d (#358)
t0mdavid-m Apr 4, 2026
b185cf0
Claude/kubernetes migration plan kq jw d (#359)
t0mdavid-m Apr 4, 2026
8c03310
Claude/fix mzml files validation y zfla (#361)
t0mdavid-m Apr 20, 2026
4069e11
Fix contrib tag (#360)
t0mdavid-m Apr 20, 2026
eae014b
Add Kubernetes deployment docs and refactor Claude skills (#362)
t0mdavid-m Apr 20, 2026
12f8e0d
ci: add ghcr-cleanup workflow (scheduled disabled, dry-run default)
t0mdavid-m Apr 20, 2026
0633790
ci: scaffold build-and-test workflow with lint-manifests job
t0mdavid-m Apr 20, 2026
aa585c2
ci: add build job skeleton with matrix, buildx, ghcr login
t0mdavid-m Apr 20, 2026
7a2dc86
ci: add metadata extraction, build-push, and registry cache
t0mdavid-m Apr 20, 2026
e7f6c08
ci: add kind integration steps to build job
t0mdavid-m Apr 20, 2026
fca45ee
Merge pull request #363 from OpenMS/ci/unify-docker-workflows
t0mdavid-m Apr 20, 2026
6a2a7ae
ci: lowercase image name for OCI cache refs
t0mdavid-m Apr 20, 2026
d8d3d03
Merge pull request #364 from OpenMS/ci/fix-lowercase-cache-ref
t0mdavid-m Apr 20, 2026
ebef5df
ci: don't pass unprefixed local tag to buildx push
t0mdavid-m Apr 20, 2026
fa46191
Merge pull request #365 from OpenMS/ci/fix-local-tag-push-to-hub
t0mdavid-m Apr 20, 2026
dd32bd1
ci: delete old docker workflows now superseded by build-and-test
t0mdavid-m Apr 20, 2026
9ce9585
k8s: pin overlay image tag to main-full (new CI scheme)
t0mdavid-m Apr 20, 2026
93fb2a4
docs(skill): update k8s deploy skill for unified CI workflow
t0mdavid-m Apr 20, 2026
29d94b4
docs(k8s): update deployment doc for unified CI workflow
t0mdavid-m Apr 20, 2026
e1cc9d4
Merge pull request #366 from OpenMS/ci/cutover-old-workflows
t0mdavid-m Apr 20, 2026
1c183d0
ci: pin container-retention-policy to v3.0.1
t0mdavid-m Apr 20, 2026
bd9bf5e
Merge pull request #367 from OpenMS/ci/pin-retention-action-to-v3.0.1
t0mdavid-m Apr 20, 2026
859e481
fix(docker): stop cache-busting on GITHUB_TOKEN
t0mdavid-m Apr 21, 2026
8f25b59
Merge pull request #368 from OpenMS/ci/fix-dockerfile-token-cache-bust
t0mdavid-m Apr 21, 2026
f3c21a5
docs: fix typo (Gihub -> GitHub) in Dockerfile comments
t0mdavid-m Apr 21, 2026
cf35e62
Merge pull request #369 from OpenMS/ci/cache-smoke-test
t0mdavid-m Apr 21, 2026
bbc43f4
ci: enable scheduled GHCR cleanup (weekly Sun 03:00 UTC)
t0mdavid-m Apr 21, 2026
25ed32c
Merge pull request #370 from OpenMS/ci/enable-ghcr-cleanup-cron
t0mdavid-m Apr 21, 2026
072161a
k8s: serve template app on both .de and .org TLDs
t0mdavid-m Apr 21, 2026
d0a1fde
ci: integration-test both .de and .org hosts on nginx and traefik
t0mdavid-m Apr 21, 2026
b970ce7
ci: enable kind to bind workspace PVC and clean up port-forwards
t0mdavid-m Apr 21, 2026
9933465
skill(configure-k8s-deployment): document dual-host overlay edit
t0mdavid-m Apr 21, 2026
0e9ceca
skill(configure-k8s-deployment): fix markdown rendering and clarify n…
t0mdavid-m Apr 21, 2026
69e655f
docs(kubernetes-deployment): document dual-host serving
t0mdavid-m Apr 21, 2026
b28b1f1
docs(kubernetes-deployment): fix stale job count and missing kind pat…
t0mdavid-m Apr 21, 2026
e840358
ci: use nginx Ingress hostnames for nginx-job curl assertions
t0mdavid-m Apr 21, 2026
636da9d
Merge pull request #372 from OpenMS/feature/dual-host-k8s
t0mdavid-m Apr 21, 2026
b69a2e5
k8s: mount admin password from streamlit-secrets Secret
claude Apr 22, 2026
594a83a
fix errors
t0mdavid-m Apr 24, 2026
e5d8044
ci: bump pyopenms to 3.5.0 and pin python 3.10 to match Dockerfile
t0mdavid-m Apr 24, 2026
64dfed8
fix(view): use pyopenms 3.5 get_df API instead of unreleased to_df
t0mdavid-m Apr 24, 2026
6f9c692
Merge pull request #374 from OpenMS/fix_pyopenms_errors
t0mdavid-m Apr 24, 2026
d78d8b8
Merge remote-tracking branch 'origin/main' into claude/hide-demo-pass…
t0mdavid-m Apr 24, 2026
8c6f869
fix(k8s): mount streamlit-secrets as directory so optional: true works
t0mdavid-m Apr 24, 2026
2cb4813
docs(k8s): add streamlit-secrets example to template-app overlay
t0mdavid-m Apr 24, 2026
971cfdd
Merge pull request #373 from OpenMS/claude/hide-demo-password-uB77g
t0mdavid-m Apr 24, 2026
6c61365
k8s: two-tier scheduling via Kustomize components + LimitRange
claude Apr 24, 2026
11ff5cd
Merge branch 'main' into claude/parallel-webapp-memory-optimization-R…
t0mdavid-m Apr 24, 2026
5870c34
ci(k8s): label kind node to match the overlay's memory tier
claude Apr 24, 2026
8abb90a
Merge branch 'claude/parallel-webapp-memory-optimization-RoNnJ' of ht…
claude Apr 24, 2026
0bd2ccf
k8s: move streamlit-secrets.yaml.example into overlays/prod/
claude Apr 24, 2026
2f28ed9
ci(k8s): two-node kind cluster with both tier labels
claude Apr 24, 2026
43c300b
ci(k8s): clear control-plane NoSchedule taint in two-node kind config
claude Apr 24, 2026
64f43e2
Merge pull request #375 from OpenMS/claude/parallel-webapp-memory-opt…
t0mdavid-m Apr 24, 2026
4ab1288
k8s: store demo workspaces on the workspaces PVC
claude Apr 24, 2026
5bd5898
Merge pull request #376 from OpenMS/claude/demo-workspace-storage-k8s…
t0mdavid-m Apr 24, 2026
391ed16
k8s: ship streamlit-secrets by default, hide admin UI when empty
claude Apr 24, 2026
3387b9c
Merge pull request #377 from OpenMS/claude/fix-streamlit-secrets-61u1H
t0mdavid-m Apr 24, 2026
0cce837
ci: reuse built docker images across ingress tests
claude Apr 24, 2026
eeb8e3f
ci: run test-traefik against both image variants
claude Apr 25, 2026
c627256
ci: harden ingress-test wait/curl flow for slow simple deployments
claude Apr 26, 2026
5901c90
Rework configure-k8s-deployment skill as an interview
t0mdavid-m Apr 26, 2026
03eff39
Merge pull request #378 from OpenMS/claude/reuse-docker-images-ci-DNvEO
t0mdavid-m Apr 26, 2026
8e096ac
Merge pull request #379 from OpenMS/claude/review-k8s-skill-UtYEm
t0mdavid-m Apr 26, 2026
286fd0b
Merge remote-tracking branch 'template/main'
t0mdavid-m Apr 26, 2026
a732de0
fix(ci): unblock CI after template merge
t0mdavid-m Apr 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions .claude/skills/add-presets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Add Parameter Presets

Add or modify parameter presets for a TOPP workflow in `presets.json`.

## Instructions

1. **Ask the user** for:
- Which workflow the presets are for
- Preset names and descriptions
- Parameter values for each preset (TOPP tool parameters and/or custom widget parameters)

2. **Determine the workflow key** from the display name:
- Convert to lowercase, replace spaces with hyphens
- Example: "TOPP Workflow" -> "topp-workflow"
- Example: "Metabolite Analysis" -> "metabolite-analysis"

3. **Read or create `presets.json`** at the repository root. Add entries following this schema:

```json
{
"workflow-name": {
"Preset Name": {
"_description": "Tooltip text shown on hover over the preset button",
"TOPPToolName": {
"algorithm:section:param_name": value
},
"_general": {
"custom-widget-key": value
}
}
}
}
```

4. **Verify the result** by checking that:
- Workflow name key matches the name passed to `WorkflowManager.__init__()` (lowercased, hyphenated)
- TOPP tool names match those used in `input_TOPP()` calls
- Parameter paths use colon-separated format matching the TOPP tool's .ini structure
- `_general` keys match widget keys from `input_widget()` calls
- JSON is valid

## Schema Rules

- **Workflow key**: lowercase, hyphens — must match `WorkflowManager("Display Name", ...)` converted
- **`_description`**: optional tooltip text for the preset button
- **TOPP tool keys**: dictionary name must exactly match the TOPP tool name (e.g., `"FeatureFinderMetabo"`)
- **TOPP parameter paths**: colon-separated, e.g., `"algorithm:common:noise_threshold_int"`
- **`_general`**: overrides for custom `input_widget()` keys (non-TOPP parameters)
- **Keys starting with `_`** are metadata and not applied as tool parameters

## How Presets Work at Runtime

1. Preset buttons auto-appear in `parameter_section()` via `StreamlitUI.preset_buttons()`
2. Only presets matching the current workflow name are displayed
3. Clicking a preset updates `params.json` in the workspace and refreshes the UI
4. If no `presets.json` exists or no presets match, no buttons are shown

## Reference Files

- Existing presets: `presets.json`
- Preset loading: `src/workflow/ParameterManager.py`
- Preset buttons: `src/workflow/StreamlitUI.py` — `preset_buttons()` method
- Documentation: `docs/build_app.md` (Parameter Presets section)

## Example

For a workflow initialized as `super().__init__("Feature Analysis", ...)`:

```json
{
"feature-analysis": {
"High Sensitivity": {
"_description": "Optimized for detecting low-abundance features",
"FeatureFinderMetabo": {
"algorithm:common:noise_threshold_int": 500.0,
"algorithm:common:chrom_peak_snr": 2.0
}
},
"Fast Processing": {
"_description": "Quick analysis with relaxed thresholds",
"FeatureFinderMetabo": {
"algorithm:common:noise_threshold_int": 2000.0
},
"_general": {
"run-python-script": false
}
}
}
}
```

## Checklist

- [ ] Workflow key is lowercase with hyphens, matching the WorkflowManager name
- [ ] Each preset has a descriptive `_description`
- [ ] TOPP tool names match exactly
- [ ] Parameter paths use colon-separated format
- [ ] `presets.json` is valid JSON
118 changes: 118 additions & 0 deletions .claude/skills/add-python-tool.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# Add a Python Analysis Tool

Add a custom Python analysis script for MS data processing to `src/python-tools/` with auto-generated UI.

Python tools handle analysis steps that aren't covered by TOPP tools — e.g., exporting consensus features to DataFrames, computing differential expression statistics, custom metabolite annotation, or post-processing identification results.

## Instructions

1. **Ask the user** for:
- Tool name (lowercase, hyphens for spaces)
- What the tool does
- Input/output file types
- Parameters: name, type, default value, constraints

2. **Create the tool script** at `src/python-tools/<name>.py`:

```python
import json
import sys

DEFAULTS = [
# Hidden I/O parameters (always include these)
{"key": "in", "value": [], "help": "Input files.", "hide": True},
{"key": "out", "value": [], "help": "Output files.", "hide": True},
# Visible parameters — examples of each type:
{
"key": "threshold",
"value": 1000.0,
"name": "Intensity Threshold",
"help": "Minimum intensity for filtering.",
"min": 0.0,
"max": 100000.0,
"step_size": 100.0,
},
{
"key": "method",
"value": "default",
"name": "Processing Method",
"options": ["default", "advanced", "custom"],
"help": "Which algorithm to use.",
},
{
"key": "num-features",
"value": 10,
"name": "Number of Features",
"widget_type": "slider",
"min": 1,
"max": 100,
"step_size": 1,
"help": "How many features to report.",
},
{
"key": "advanced-setting",
"value": 5,
"name": "Advanced Setting",
"help": "Only shown in advanced mode.",
"advanced": True,
},
{
"key": "enabled",
"value": True,
"name": "Enable Processing",
},
]


def get_params():
if len(sys.argv) > 1:
with open(sys.argv[1], "r") as f:
return json.load(f)
else:
return {}


if __name__ == "__main__":
params = get_params()
# Tool logic here — read inputs, process, write outputs
```

3. **Wire into a workflow** by adding to the workflow's `configure()` and `execution()` methods:

```python
# In configure():
self.ui.input_python("tool_name")

# In execution():
self.executor.run_python("tool_name", {"in": input_files})
```

## DEFAULTS Metadata Keys

| Key | Required | Type | Description |
|-----|----------|------|-------------|
| `key` | Yes | str | Unique identifier for the parameter |
| `value` | Yes | any | Default value (type determines widget: bool=checkbox, str with options=selectbox, number=number_input) |
| `name` | No | str | Display name in the UI |
| `help` | No | str | Tooltip text |
| `hide` | No | bool | If `True`, parameter is not shown in UI (use for in/out files) |
| `options` | No | list | Valid choices — renders as selectbox |
| `min` | No | number | Minimum value for numeric inputs |
| `max` | No | number | Maximum value for numeric inputs |
| `step_size` | No | number | Step size for numeric inputs |
| `widget_type` | No | str | Override widget type: `"slider"`, `"textarea"`, `"number"`, `"text"`, etc. |
| `advanced` | No | bool | If `True`, only shown when user expands advanced parameters |

## Reference Files

- Example tool: `src/python-tools/example.py`
- Another example: `src/python-tools/export_consensus_feature_df.py`
- UI generation: `src/workflow/StreamlitUI.py` — `input_python()` method
- Execution: `src/workflow/CommandExecutor.py` — `run_python()` method

## Checklist

- [ ] Script created in `src/python-tools/` with DEFAULTS list
- [ ] `get_params()` function and `__main__` block included
- [ ] `in` and `out` keys in DEFAULTS with `"hide": True`
- [ ] Wired into workflow via `self.ui.input_python()` and `self.executor.run_python()`
134 changes: 134 additions & 0 deletions .claude/skills/add-visualization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# Add MS Data Visualization

Add mass spectrometry data visualizations using pyopenms-viz or OpenMS-Insight components.

MS data has specialized visualization needs: mass spectra (m/z vs intensity stick plots), chromatograms (RT vs intensity), 2D peak maps (RT vs m/z heatmaps), isotope patterns, fragment ion annotations, and statistical plots like volcano plots for differential expression.

## Instructions

1. **Ask the user** for:
- What data to visualize (spectra, chromatograms, peak maps, tables, heatmaps)
- Data size expectations (small/medium vs. large datasets with millions of points)
- Whether interactive cross-component linking is needed

2. **Choose the right library**:

| Use Case | Library | Why |
|----------|---------|-----|
| Quick spectrum/chromatogram/peak map plots | **pyopenms-viz** | Simple one-liner API, publication quality |
| Large datasets (millions of points) | **OpenMS-Insight** | Server-side pagination, intelligent downsampling |
| Interactive tables with pagination | **OpenMS-Insight** `Table` | Tabulator.js, CSV export, server-side filtering |
| Cross-linked views (click table row → highlight in plot) | **OpenMS-Insight** | Shared `link_id` across components |
| Standard Plotly plots (bar, scatter, etc.) | **plotly.express** directly | Already available, use with `show_fig()` |

3. **Implement the visualization** in a page or workflow results section.

## pyopenms-viz Patterns

pyopenms-viz extends pandas DataFrames with MS-specific plot accessors. Always use the `plotly` backend for Streamlit.

```python
import pyopenms_viz
from src.common.common import show_fig

# Mass spectrum (stick plot from mz/intensity columns)
fig = df.plot.ms_spectrum(backend="plotly", title="MS1 Spectrum")
show_fig(fig, "spectrum-plot")

# 2D peak map (RT vs m/z, colored by intensity)
fig = df.plot.peak_map(backend="plotly")
show_fig(fig, "peak-map")

# Chromatogram (RT vs intensity)
fig = df.plot.chromatogram(backend="plotly")
show_fig(fig, "chromatogram")

# Mobilogram (ion mobility vs intensity)
fig = df.plot.mobilogram(backend="plotly")
show_fig(fig, "mobilogram")
```

**Key points:**
- DataFrame must have appropriate columns (e.g., `mz`, `intensity` for spectra)
- Always use `backend="plotly"` in Streamlit context
- Use `show_fig()` from `src/common/common.py` for consistent display with download buttons
- Import `pyopenms_viz` to register the plot accessors (even if not used directly)

## OpenMS-Insight Patterns

OpenMS-Insight provides Vue.js-backed interactive components optimized for large MS datasets.

```python
from openms_insight import Table, LinePlot, Heatmap, VolcanoPlot, SequenceView

# Interactive table with server-side pagination
Table(df, key="results-table", page_size=50)

# Stick-style mass spectrum
LinePlot(df, x="mz", y="intensity", key="spectrum-plot")

# 2D heatmap for large datasets (auto-downsampling)
Heatmap(df, x="rt", y="mz", z="intensity", key="peak-heatmap")

# Volcano plot for differential expression
VolcanoPlot(df, x="log2fc", y="neg_log10_pval", key="volcano")

# Peptide sequence with fragment ion annotations
SequenceView(sequence="PEPTIDER", ions=ion_df, key="sequence")
```
Comment on lines +61 to +78
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Correct the OpenMS-Insight API pattern to match the actual implementation.

The documented pattern shows direct data passing (Table(df, key="results-table")), but the actual implementation in the codebase uses a cache-based approach. Components are initialized with cache_id and cache_path, then called with state_manager and other parameters.

✅ Correct pattern from content/results_database_search.py
from openms_insight import Table, LinePlot, Heatmap, SequenceView, StateManager

# Initialize state manager for cross-component linking
state_manager = StateManager()

# Load components from cache (no data parameter needed)
table = Table(cache_id=f"table_{cache_id_prefix}", cache_path=str(cache_dir))
heatmap = Heatmap(cache_id=f"heatmap_{cache_id_prefix}", cache_path=str(cache_dir))
line_plot = LinePlot(cache_id=f"lineplot_{cache_id_prefix}", cache_path=str(cache_dir))

# Render components by calling them
st.subheader("PSM Table")
table(state_manager=state_manager, height=533)

st.subheader("MS2 Spectrum")
line_plot(key=f"lineplot_{cache_id_prefix}", state_manager=state_manager, height=450)

Key differences:

  • Components are initialized with cache_id and cache_path (not data)
  • Components are rendered by calling them as functions
  • state_manager is passed when rendering, not link_id
  • key is passed during rendering, not initialization
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/skills/add-visualization.md around lines 61 - 78, Update the example
to use the cache-based component pattern used in the codebase: import and
instantiate StateManager, create components with cache_id and cache_path (e.g.,
Table(cache_id=..., cache_path=...), Heatmap(...), LinePlot(...),
SequenceView(...)) rather than passing data, then render them by calling the
component instances with state_manager and runtime args (e.g., key, height) —
for example create state_manager = StateManager(), instantiate
table/heatmap/line_plot variables with cache_id/cache_path, and call
table(state_manager=state_manager, height=...), line_plot(key=...,
state_manager=state_manager, height=...), etc.


### Cross-Component Linking

Link components so selections in one update another:

```python
# Selecting a row in the table highlights the corresponding point in the plot
Table(df, key="linked-table", link_id="feature_id")
LinePlot(df, x="mz", y="intensity", key="linked-plot", link_id="feature_id")
Heatmap(df, x="rt", y="mz", z="intensity", key="linked-heatmap", link_id="feature_id")
```

All components sharing the same `link_id` column are automatically synchronized.
Comment on lines +84 to +91
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Correct the cross-component linking pattern to use StateManager.

The documented pattern shows using a link_id parameter, but the actual implementation uses StateManager for cross-component synchronization. The link_id column approach is not consistent with the real codebase.

✅ Correct linking pattern
from openms_insight import Table, LinePlot, Heatmap, StateManager

# Initialize state manager ONCE for all linked components
state_manager = StateManager()

# Initialize components from cache
table = Table(cache_id="table_id", cache_path=str(cache_dir))
line_plot = LinePlot(cache_id="lineplot_id", cache_path=str(cache_dir))
heatmap = Heatmap(cache_id="heatmap_id", cache_path=str(cache_dir))

# Render components with the SAME state_manager instance
table(state_manager=state_manager, height=533)
line_plot(state_manager=state_manager, height=450)
heatmap(state_manager=state_manager, height=350)

Selections in one component automatically update all components sharing the same state_manager instance.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/skills/add-visualization.md around lines 84 - 91, The docs show
using a deprecated link_id parameter for cross-component syncing; update the
example to create a single StateManager instance and pass it into each component
instead: import and instantiate StateManager, initialize Table, LinePlot,
Heatmap with their cache_id/cache_path constructors (Table, LinePlot, Heatmap)
and then render each by calling the component with the same state_manager
argument (e.g., table(state_manager=state_manager, ...),
line_plot(state_manager=state_manager, ...),
heatmap(state_manager=state_manager, ...)) so selections synchronize via the
StateManager rather than a link_id column.


## Integration in Workflow Results

Add visualization to a workflow's `results()` method:

```python
@st.fragment
def results(self) -> None:
result_file = Path(self.workflow_dir, "results", "step-name", "output.tsv")
if result_file.exists():
df = pd.read_csv(result_file, sep="\t")

# pyopenms-viz for spectrum plots
import pyopenms_viz
fig = df.plot.ms_spectrum(backend="plotly")
show_fig(fig, "results-spectrum")

# Or OpenMS-Insight for interactive exploration
from openms_insight import Table
Table(df, key="results-table")
else:
st.warning("No results found. Please run the workflow first.")
```
Comment on lines +98 to +114
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Align the workflow integration example with the cache-based pattern.

The OpenMS-Insight example in the results() method shows direct data passing (Table(df, key="results-table")), which is inconsistent with the cache-based pattern used throughout the actual codebase (see content/results_database_search.py).

🔄 Suggested alignment with actual pattern
`@st.fragment`
def results(self) -> None:
    cache_dir = Path(self.workflow_dir, "results", "insight_cache")
    
    # pyopenms-viz for spectrum plots
    result_file = Path(self.workflow_dir, "results", "step-name", "output.tsv")
    if result_file.exists():
        df = pd.read_csv(result_file, sep="\t")
        import pyopenms_viz
        fig = df.plot.ms_spectrum(backend="plotly")
        show_fig(fig, "results-spectrum")
    
    # OpenMS-Insight for interactive exploration (cache-based)
    cache_id = "results_table"
    if (cache_dir / f"table_{cache_id}").is_dir():
        from openms_insight import Table, StateManager
        state_manager = StateManager()
        table = Table(cache_id=f"table_{cache_id}", cache_path=str(cache_dir))
        table(state_manager=state_manager, height=533)
    else:
        st.warning("No cached results found. Please run the workflow first.")

Alternatively, if direct data passing is a valid alternative pattern, clarify when to use each approach.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/skills/add-visualization.md around lines 98 - 114, The results()
fragment should use the cache-based OpenMS-Insight pattern instead of passing
DataFrame directly: create cache_dir = Path(self.workflow_dir, "results",
"insight_cache"), set a cache_id (e.g. "results_table"), check for (cache_dir /
f"table_{cache_id}").is_dir() and if present instantiate StateManager() and
Table(cache_id=f"table_{cache_id}", cache_path=str(cache_dir)) and call
table(state_manager=state_manager, height=533); keep the pyopenms_viz spectrum
plotting (df.plot.ms_spectrum + show_fig) but move the Table usage to the cached
flow and show st.warning when the cache is missing.


## Reference Files

- Display utilities: `src/common/common.py` — `show_fig()`, `show_table()`
- Dependencies: `requirements.txt` — `pyopenms-viz`, `openms-insight`
- Example workflow results: `src/Workflow.py` — `results()` method

## Library Repositories

- **pyopenms-viz**: OpenMS/pyopenms-viz — pandas DataFrame `.plot()` extension with matplotlib/bokeh/plotly backends
- **OpenMS-Insight**: t0mdavid-m/openms-insight — Vue.js interactive Streamlit components with caching and downsampling

## Checklist

- [ ] Correct library chosen for the use case
- [ ] Dependencies present in `requirements.txt`
- [ ] `show_fig()` used for pyopenms-viz plots (consistent download/export behavior)
- [ ] `backend="plotly"` specified for all pyopenms-viz plots
- [ ] Unique `key=` values for all OpenMS-Insight components
- [ ] Cross-component linking set up if multiple views of same data
Loading
Loading