Skip to content

Edgy release - #1890

Merged
ESCRI11 merged 582 commits into
develfrom
edgy
Sep 8, 2026
Merged

ESCRI11 merged 582 commits into
develfrom
edgy

Conversation

@ESCRI11

@ESCRI11 ESCRI11 commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

PR for edgy release

ivokwee and others added 30 commits July 30, 2026 00:00
fix: show p-values alongside q-values in DE table
…ypes

feat: metadata upload on all datatypes
- reports/images listed 6/2 candidate models each, but only choices[[1]]
  is ever selected since the model pickers are hidden for the
  bigomics-managed backend (appsettings_ui.R)
- collapse both arrays to the single model that was actually running,
  removing the unused entries so the policy reflects reality
- swap reports + copilot_balanced to openrouter:deepseek/deepseek-v4-flash
  and copilot_deep to openrouter:openai/gpt-5.6-terra in the bigomics
  provider block of etc/ai_model_policy.json (images left unchanged)
- update the test-appsettings-ai.R BigOmics assertions and test name to
  match the new ids so the fixture stops hardcoding the old gpt-5.4-nano/mini
- refresh the stale copilot_run_controller.R tier-table comment naming the
  managed-backend models (omicsplayground-p6wk)
…omics"

- copilot_restore_controller passed the "bigomics" sentinel straight to
  ovi_restore(), whose ovi_resolve_copilot_model() then hit the BYOK catalog
  branch and errored (ai_select_model has no "bigomics" provider), crashing
  restore of managed sessions
- add .copilot_restore_provider(): map managed -> "openai" (drop key), pass a
  real BYOK provider + key through, mirroring .copilot_agent_build_args so
  restore resolves whatever provider/model the user has configured now
- fix the restore-controller test bootstrap (unsourced .copilot_ai_provider)
  and cover managed + BYOK provider resolution
- deepseek-v4-flash and openai/gpt-5.6-terra only default reasoning_effort
  to medium as of omicsai 0.3.8; the old >=0.3.2 floor let a stale install
  silently resolve those models as plain chat, hard-erroring the first
  time reasoning_effort was passed explicitly (surfaced during the
  chores/update-ai-models design review)
- the standalone suite sources appsettings_server.R but not
  AuthenticationModule_functions.R, so every server-invoking test errored
  with "could not find function ai_byok_allowed" before reaching its
  assertions (shipped broken with the BYOK feature: 8 other app-globals are
  stubbed, this one was missed)
- add the entitlement helper to the bootstrap block, mirroring the real
  definition exactly, so the 14 affected tests run their real code paths
  (make_auth leaves level unset -> "" -> BYOK allowed)
- surfaced while reviewing chores/update-ai-models, which touches this file
- omicsai 0.3.8 added gpt-5.6-sol/terra/luna to its openai catalog; the
  openai provider policy only excludes gpt-5.5, so the three new models
  now legitimately appear in the reports/copilot menus alongside
  gpt-5.4-nano/mini and gpt-4o/gpt-4o-mini
- update the two stale fixtures (live-catalog repopulate, and the
  empty-live-results fallback) to the new 7-item order rather than
  curating the models out of the policy - product decision: BYOK users
  get the extra choice
The "Basic menu" switch (App settings) swapped the sidebar for a 5-tab
subset but changed nothing else: every board still showed its full
settings panel and every tab.

Menu
- basic_menu_tree keys now equal the tab name, so createMenu() emits a
  flat sidebar item instead of a collapsible group holding one identical
  child ("Cluster Samples > Cluster Samples").

Settings
- .advanced-option marks the blocks BASIC greys out: FDR/logFC and the
  Options accordion on Differential expression and Geneset Enrichment,
  the Options accordion on DataView and Pathway analysis. Driven by a
  body.basic-mode class rather than shinyjs::disable() because the boards
  are inserted lazily, after the toggle observer has already run, so a
  disable() at toggle time would be lost.
- Greyed accordions are force-expanded: the settings are locked, but the
  user should still see which ones are in effect.
- Differential expression shows only its Overview tab. !important because
  tab_control() calls shiny::showTab(), which writes an inline display.
  Switching to BASIC also resets the tabset, since the active tab may be
  one that is about to be hidden.

Not included: skipping the load of boards unreachable in BASIC. Shiny
already suspends outputs in hidden elements (suspendWhenHidden defaults
TRUE) and bigdash's toggleTab fires the `hidden` event that drives it, so
the sibling boards in a module never render while their tab is inactive.
There is no rendering left to save there.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…options

- basic menu is now DataView, Cluster Samples, Differential expression
- Cluster Samples: grey out the "Filter options" and "Advanced options"
  accordions (hm_filter_accordion, hm_options_accordion)

The .advanced-option markers on Geneset Enrichment and Pathway analysis
stay: they are keyed on the body.basic-mode class, not on the menu, so
they stay correct if either board returns to the basic menu. Inert until
then.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
They stay collapsed as usual; only the grey-out remains. Takes the
chevron rotate with it, which only existed to match the forced-open
state.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Same treatment as Differential expression: Overview is the first panel of
#dataview-tabs, so the existing selector just gains a second target.
Switching to BASIC also resets the tabset, since the active tab may be
one that is about to be hidden.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This reverts commit 4661ce3. Supervisor wants the greyed accordions
expanded after all, so BASIC users can see which locked settings are in
effect. Restores the forced-open rule and the matching chevron rotate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- generate AI text reports (de, pathways) and infographics (combined,
  de, pathways) into pgx$ai via playbase::pgx.update_reports() /
  pgx.update_infographics(), filling slots that were never populated
  for the bundled example dataset
- wgcna report generation failed on this dataset (playbase bug,
  tracked separately) and was skipped rather than aborting the batch
ESCRI11 and others added 25 commits September 2, 2026 11:06
fix(home): stop the launcher overflowing on 14" laptops
Gate DataView Settings tab behind DEVMODE
…launcher dev-gate

The single "app" OPG instance's sidebar DOM is built once per session
by opg_ui()'s createMenu() -- opg_server.R's tab_control() only
shows/hides those existing nodes at runtime (bigdash.filterTabs()), it
can't turn a collapsible group into flat items or back. So an earlier
attempt to promote MultiOmics's own boards (MOFA, multiGSEA, SNF, ...)
out of their collapsible "MultiOmics" submenu by only changing
opg_multiomics_menu_tree() (opg_server.R's reactive filtering tree)
never reached the DOM: ui.R's opg_ui("app") call still built the
sidebar from the plain, grouped opg_menu_tree().

- opg_ui.R: extract the promotion into opg_promote_multiomics(), a
  general helper that splits any tree's "MultiOmics" group into flat,
  single-board entries at the same position, leaving every other
  group (and its key name) untouched -- opg_menu_tree() itself stays
  unchanged, so admin_ui.R's Basic Menu picker (which labels boards
  "<GroupName> - <BoardTitle>" off opg_menu_tree()'s own keys) still
  shows "MultiOmics - LASAGNA" etc. rather than per-board group names.
  opg_multiomics_menu_tree() now just builds its restricted subset and
  reuses the same helper.
- ui.R: opg_ui("app", ...) now passes
  menu_tree = opg_promote_multiomics(opg_menu_tree()) -- this is what
  actually builds the sidebar DOM, so the flat layout applies to both
  the Playground and MultiOmics views alike (board visibility is still
  separately gated by tab_control()'s dataset-datatype check, so
  MultiOmics boards still only show for multi-omics datasets).
- launcher_server.R: launch_multiomics no longer shows the
  "Developers only!" alert or requires opt$DEVMODE.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cag8uYDzGgwXxseRgfwLmf
…sidebar divider

Switching between the MultiOmics and Playground views left a visibly
thicker line between SystemsBio and Standard WGCNA -- traced to two
separate bugs in the same area:

1. opg_server.R has two independent observers that both call
   tab_control(): one keyed on env$trigger_on_change_dataset() (fires
   once the new dataset is actually loaded) and one keyed on
   menu_tree() alone. opg_view() -- and so menu_tree() -- flips the
   instant a launcher tile is clicked, well before PGX$X/PGX$datatype
   catch up to the dataset that eventually loads to match it. The
   menu_tree()-only observer therefore fired tab_control() once right
   on that flip, computing a keep-list from the *new* view against the
   *still-old* dataset (e.g. showing every SystemsBio/WGCNA board at
   once) before the dataset-load observer corrected it a moment later.
   Guarded it to skip while the loaded dataset's type doesn't match
   the current view -- safe either way, since the other observer
   re-runs tab_control() once a matching dataset actually loads.

2. The real, persistent culprit: bigdash.showTabs(session) (called
   once a dataset finishes loading) fires bigdash's client-side
   'show-tabs' message, which blanket-shows every direct child of
   .sidebar-content (anything not .collapse/.nodisp) after its own 1s
   delay. That includes a flat, individually-filtered sidebarItem()'s
   own <hr> divider (a direct child) even though its nested
   <p class="tab-trigger"> (one level deeper, untouched by that
   blanket pass) correctly stays hidden -- silently re-showing the
   divider for an item tab_control() had just hidden (e.g. a
   MultiOmics board while viewing a single-omics dataset), until some
   unrelated tab_control() run happened to fix it. Re-run tab_control()
   just after that delay elapses so the two stay in sync.

Confirmed via jQuery show/hide/toggle instrumentation and repeated
live cycles (Playground <-> MultiOmics, mox-brca <-> example-data)
that the divider stays a single, uniform-thickness line throughout.
No changes to the bigdash package itself.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cag8uYDzGgwXxseRgfwLmf
ui.R has no uiOutput("current_dataset") (or any navheader-style div)
anywhere -- this output, its dataset_click observer, and the
removeUI(selector = "#current_dataset > #spinner-container") cleanup
call never had anything to render into or select. The actual
dataset-name popup users see comes entirely from
components/ui/ui-omicsboard.R's OmicsBoardUI()/OmicsBoard() pair,
which every board wires up through its own namespaced
uiOutput(ns("current_dataset")).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cag8uYDzGgwXxseRgfwLmf
pgx$wgcna$report$infographic/$bullets (the old ai-old-wgcnareport.R
schema) is never populated by the current pipeline -- nothing calls
those generator functions anymore -- so the popup's infographic branch
was permanently dead, always falling through to the plain pgx.info()
text popup.

Point it at pgx$ai$combined$infographic instead: the v1 AI-report
schema (playbase::pgx.update_infographics(), ai-infographics.R),
where "combined" is the dataset-wide report -- labelled "Summary" in
AI Studio's own infographic tab (.AI_INFOGRAPHIC_LABELS). That field
holds raw PNG bytes plus a status/error, not a decoded raster array,
so decode via png::readPNG() (which accepts a raw vector directly)
before handing it to ui.showImageModal(), which still expects one.
Falls back to the text-info popup whenever the infographic is missing,
not yet generated, errored, or fails to decode.

The old schema's short "bullets" field (a few summary lines meant for
the modal footer) has no equivalent in the new one -- pgx$ai$combined
$report is a full multi-section markdown write-up, not caption-sized
-- so the footer is now just a static "Summary infographic" label.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cag8uYDzGgwXxseRgfwLmf
Adds a small, generic modal helper -- ui.showTabsetModal(tabs, ...) in
ui-modalUI.R -- that lays out a named list of tab title -> UI content
as a bslib::navset_tab() inside modalDialog2(). A single-tab list skips
the tab switcher entirely and shows that one tab's content directly,
since there's nothing to switch between.

Factored the image-embedding half of ui.showImageModal() out into
ui.imageTag() (ui-startupModal.R) -- a raster array to a base64 <img>
tag -- so it can be reused as one tab's content without going through
a whole separate modal.

The dataset-name popup (ui-omicsboard.R) now uses ui.showTabsetModal()
with an "Infographic" tab (pgx$ai$combined$infographic, decoded via
png::readPNG()) plus an "Info" tab (playbase::pgx.info()) when an
infographic is available, or just the "Info" tab alone -- rendered
without a tab switcher -- when it isn't.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cag8uYDzGgwXxseRgfwLmf
Switching tabs previously resized the whole modal to whatever the
newly-active tab's content happened to need. ui.showTabsetModal() now
wraps its tab area in a fixed-height, scrollable box (body_height,
default "65vh") whenever there's more than one tab -- the same box
regardless of which tab is showing, so the dialog itself never moves.
A single-tab modal is untouched (nothing to switch between).

ui.imageTag() gains a fit=TRUE mode: instead of a fixed pixel width
with height following the image's own aspect ratio, it scales via CSS
(max-width:100%, object-fit:contain) to fit whatever space it's
dropped into, capped by an explicit max_height (matched to the
tabset's body_height -- a CSS percentage-height chain down through
bslib's own tab-panel wrapper divs isn't guaranteed, so this uses the
same fixed value on both ends instead of relying on one).

The dataset-name popup's Infographic tab now uses fit=TRUE with the
same body_height passed to ui.showTabsetModal(), so the infographic
scales to fill the fixed tab area instead of rendering at a flat
1000px width.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cag8uYDzGgwXxseRgfwLmf
- force the session consent to FALSE whenever the AI config is locked or the
  deployment is unlicensed, in the login seed and the write observer as well
  as the greying observer: a disabled switch keeps whatever value it had, so a
  user who opted in before the lock stayed opted in behind a control they
  could no longer reach. The stored record is left untouched, since a
  deployment lock is not the user withdrawing
- pass the live consent into ovi_restore() on both the async and sync restore
  paths, reading it from .copilot_ai_provider rather than the restore-time
  provider, which has already been rewritten to ovi's "openai" sentinel and so
  would never have matched "bigomics"
- reword the switch to name the party that actually trains: BigOmics trains no
  models, what the toggle widens is which upstream providers may be routed to
- raised in review of #1885
body_height was wrapping the entire navset (tab-header row plus
content), so the actual content area each tab's own box gave was
body_height minus however tall the tab-header row happened to be. An
image fit against the full body_height value (as the dataset-name
popup's Infographic tab does) then overflowed that smaller real box
by exactly the header's height, forcing a vertical scrollbar even
though the image itself never exceeded the intended box.

Wrap each tab's own content in the fixed height/width box instead, so
the tab-header row sits above it unaffected and every tab's content
area is identically sized (both dimensions) regardless of which tab
is active -- and an image capped at the same body_height value now
actually fits within its real box.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cag8uYDzGgwXxseRgfwLmf
Same dead-schema issue as the dataset-name popup: pgx$wgcna$report
$bullets/$infographic (ai-old-wgcnareport.R) is never populated by the
current pipeline. Points bullet_points and image.RENDER() at the v1
schema's "combined" slot instead -- the dataset-wide report, labelled
"Summary" in AI Studio's own infographic tab -- via pgx$ai$combined
$report and pgx$ai$combined$infographic$bytes.

image.RENDER() writes the infographic straight to its tempfile with
writeBin() rather than png::readPNG() + png::writePNG(): the new
schema already stores raw PNG bytes (not a decoded raster array), so
the old decode/re-encode round trip is no longer needed. Also guards
on infographic$status == "done" and non-empty bytes, matching the
popup's fallback behavior for a missing/errored/in-progress entry.

(SummaryBoardUI()/SummaryBoard() are only source()d by 00SourceAll.R
-- nothing currently mounts this board in any nav_panel or board
registry -- so this fixes the schema without addressing that.)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cag8uYDzGgwXxseRgfwLmf
…tent

Constraining each tab's own content to width:100% (previous commit)
wasn't enough -- .modal-dialog itself only gets a max-width from its
size class (e.g. modal-xl), not a fixed width, so the dialog still
sized to whichever tab's content happened to be active (a narrower
image vs. a full-width text panel) and visibly changed width when
switching tabs.

modalDialog2() gains a dialog_style param applied directly to
.modal-dialog; ui.showTabsetModal() passes dialog_style="width:100%;"
whenever there's more than one tab, pinning the dialog to its size
class's max-width consistently regardless of which tab is showing.
Single-tab modals (and every other modalDialog2() caller) are
unaffected -- dialog_style defaults to NULL.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cag8uYDzGgwXxseRgfwLmf
Pulls Highlights, Overview, and Conclusion out of the combined
report's markdown (pgx$ai$combined$report) via the new
playbase::ai_report_get_section(), one sub-heading per section, with
a small disclaimer at the bottom ("generated by AI and may contain
inaccuracies"). "Conclusion" (singular) is the canonical heading
name the combined-report prompt actually produces (omicsai's
inst/prompts/combined/combined_report_rules.md), not "Conclusions".

The tab is only added when at least one of the three sections is
actually present -- same pattern as the Infographic tab -- so a
dataset with no AI report yet still shows just Info (or
Infographic + Info). Tab order is now Infographic, AI summary, Info,
whichever apply.

Verified the section-pulling/rendering logic (all three sections
present, a partial report with only one section, no report, and a
report with none of the three headings) against a standalone copy of
ai_report_get_section(), since the installed playbase package doesn't
have the function yet -- it needs a package rebuild from
/home/kwee/Playground/repo/playbase (uncommitted there) before this
tab will actually populate in a running app.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cag8uYDzGgwXxseRgfwLmf
Was just trailing right after the section text via a normal hr()/div,
so it floated wherever the sections happened to end rather than
sitting at the bottom of the tab's fixed-height box. Wraps the tab in
a flex column (min-height:100%, filling ui.showTabsetModal()'s
body_height) with margin-top:auto on the disclaimer block, pinning it
to the bottom regardless of how much section text sits above it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cag8uYDzGgwXxseRgfwLmf
feat: add per-user consent for AI conversation data sharing
fix(ui): dataset-name popup — v1 AI schema, tabset modal, AI summary tab
- point Copilot's data_dir at auth$user_dir, the same base its chats and
  uploaded docs already derive from, instead of the shared PGX.DIR root
- the app writes a user's .pgx files under auth$user_dir, so scanning the
  root turned up only other users' folders and datasets went unfound
- auth already applies the ENABLE_USERDIR rule and every dataset write
  path agrees with its answer, so re-deriving <pgx_dir>/<email> aimed at
  an empty folder whenever that flag was off or under apache-cookie auth
- keep the per-email override for chats and uploaded docs, which Copilot
  creates itself and which need no pre-existing content
- 0.5.95 re-pins the dataset directory before each listing forward; on
  older builds the per-user data_dir introduced here would let one
  session's listing return another user's dataset names
Conflicts resolved:
- docker/Dockerfile.update: kept both sides' package installs
  (plotly.repel from edgy, WGCNAplus/isotree/collapse from master)
- VERSION, CHANGELOG*.md, FEATURES.md: kept edgy's generated files;
  they are a superset of master's (same tag sections plus 5.0-rc1) and
  match what `make changelog` produces on the current script.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXtDC1prGoyKe4cYf2hTSE
ESCRI11 and others added 2 commits September 8, 2026 10:41
Conflict resolutions:
- VERSION: keep edgy's 5.0-rc1+edgy260831
- components/board.pcsf/R/util_pcsf.R: edgy deleted the file as dead code,
  but devel's #1789 added pcsf.write_cx2() there and the PCSF panels call it.
  Restore the file with only pcsf.write_cx2(); visplot.PCSF() and
  plot.pcsf.NOTUSED() stay deleted (unreferenced in edgy).
- components/ui/ui-PlotModule.R: stays deleted; PlotModuleServer now lives in
  bigdash. devel's download.handlers argument (used by the new CX2 export) is
  ported there in bigomics/bigdash#47.
- components/board.upload/R/upload_module_preview_counts.R: keep edgy's
  unconditional datapath[1] (the filename gate was removed on purpose in
  d95d6a4) and layer devel's hPTM additions on top.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TwCeAfAF9p965ed6nZUVjx
Qsee/Bsee, Across and Mythril are badged "soon" on the launcher; the popup
now carries a notify-me link to the matching HubSpot form instead of a
dead end.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TwCeAfAF9p965ed6nZUVjx
@ESCRI11
ESCRI11 merged commit b84385d into devel Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants