fix: detect Mercury stage per-view via session name
The Mercury app (3.2.x) is a hybrid server whose kernel pool serves both the client-facing app view and JupyterLab, so the server-level MERCURY_CONFIG_DIR signal cannot tell who is looking and leaks backstage content in the app. Switch the primary stage signal to the shadow-copy session name (__mercury__ in JPY_SESSION_NAME), keeping the env var only as a --working-dir fallback. - Update CX Discovery staging.py to the per-view detection model - Ignore Mercury runtime artifacts (.mercury_sessions/, *__mercury__*) - Document the leak in remaining masters (TEI, CTM, AI Diagnostic, template) and flag the staging.py retrofit as urgent
This commit is contained in:
@@ -1,16 +1,27 @@
|
||||
"""
|
||||
Stage vs backstage — is this notebook render stakeholder-facing?
|
||||
|
||||
The Mercury CLI (``mercury --working-dir …``) exports ``MERCURY_CONFIG_DIR``
|
||||
into the server process so the widget library can locate ``config.toml``
|
||||
(see ``mercury/config.py``); every kernel that server spawns inherits it.
|
||||
JupyterLab and nbconvert kernels don't have it. That makes the variable a
|
||||
reliable signal for "the audience is looking" (the stage) versus an
|
||||
analyst session or a headless export run (backstage).
|
||||
The Mercury app (3.2.x) is a hybrid Jupyter server: the SAME server (and
|
||||
kernel pool) can serve both the client-facing app view and JupyterLab, so a
|
||||
server-level signal cannot tell who is looking. The reliable, per-view
|
||||
signal is the session name: the app runs every session against a shadow
|
||||
copy named ``<notebook>__mercury__<id>.ipynb``
|
||||
(``mercury_app/handlers.py``), and the kernel sees that path in
|
||||
``JPY_SESSION_NAME``. JupyterLab and nbconvert sessions carry the plain
|
||||
notebook path (or no session name at all).
|
||||
|
||||
Diagnostics routed through :func:`backstage` stay visible in JupyterLab
|
||||
and land in the nbconvert exports (where the machine-readable appendix
|
||||
must appear for LLM consumption) but never render in the Mercury app.
|
||||
``MERCURY_CONFIG_DIR`` is kept as a fallback: ``mercury --working-dir …``
|
||||
exports it into the server process and every kernel inherits it. It is a
|
||||
server-level signal — on such a server even JupyterLab kernels carry it, so
|
||||
diagnostics are then hidden in that Lab view too (hidden, never leaked; use
|
||||
a separate ``jupyter lab`` for analysis, which is the normal workflow). It
|
||||
is NOT set when mercury is launched without ``--working-dir``, which is why
|
||||
it cannot be the primary signal.
|
||||
|
||||
Diagnostics routed through :func:`backstage` / :func:`backstage_md` stay
|
||||
visible in JupyterLab and land in the nbconvert exports (where the
|
||||
machine-readable appendix must appear for LLM consumption) but never render
|
||||
in the Mercury app.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -20,7 +31,9 @@ from typing import Any
|
||||
|
||||
|
||||
def on_stage() -> bool:
|
||||
"""True when running under the Mercury app (stakeholder-facing)."""
|
||||
"""True when this kernel renders the client-facing Mercury app view."""
|
||||
if "__mercury__" in os.getenv("JPY_SESSION_NAME", ""):
|
||||
return True # the app's shadow-copy session
|
||||
return os.getenv("MERCURY_CONFIG_DIR") is not None
|
||||
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,14 +1,47 @@
|
||||
"""Stage/backstage detection — Mercury kernels carry MERCURY_CONFIG_DIR."""
|
||||
"""Stage/backstage detection.
|
||||
|
||||
Two signals mark the Mercury app view (see staging.py): the app's shadow
|
||||
session name (``__mercury__`` in ``JPY_SESSION_NAME`` — per-view, primary)
|
||||
and the ``MERCURY_CONFIG_DIR`` env var (server-level fallback, only set by
|
||||
``mercury --working-dir``). Either one means "a client may be looking".
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from discoverylib import staging
|
||||
|
||||
|
||||
def test_backstage_prints_only_off_stage(monkeypatch, capsys):
|
||||
@pytest.fixture(autouse=True)
|
||||
def clean_stage_env(monkeypatch):
|
||||
monkeypatch.delenv("MERCURY_CONFIG_DIR", raising=False)
|
||||
monkeypatch.delenv("JPY_SESSION_NAME", raising=False)
|
||||
|
||||
|
||||
def test_backstage_by_default(capsys):
|
||||
assert not staging.on_stage()
|
||||
staging.backstage("visible")
|
||||
assert capsys.readouterr().out == "visible\n"
|
||||
|
||||
|
||||
def test_plain_session_name_is_backstage(monkeypatch, capsys):
|
||||
# A JupyterLab or nbconvert session: the real notebook path, no marker.
|
||||
monkeypatch.setenv("JPY_SESSION_NAME", "notebooks/cx_discovery.ipynb")
|
||||
assert not staging.on_stage()
|
||||
staging.backstage("visible")
|
||||
assert capsys.readouterr().out == "visible\n"
|
||||
|
||||
|
||||
def test_mercury_shadow_session_is_stage(monkeypatch, capsys):
|
||||
# The Mercury app runs against a shadow copy: <stem>__mercury__<id>.ipynb
|
||||
monkeypatch.setenv(
|
||||
"JPY_SESSION_NAME", "notebooks/cx_discovery__mercury__545e5520.ipynb"
|
||||
)
|
||||
assert staging.on_stage()
|
||||
staging.backstage("hidden")
|
||||
assert capsys.readouterr().out == ""
|
||||
|
||||
|
||||
def test_config_dir_fallback_is_stage(monkeypatch, capsys):
|
||||
monkeypatch.setenv("MERCURY_CONFIG_DIR", "/tmp/app")
|
||||
assert staging.on_stage()
|
||||
staging.backstage("hidden")
|
||||
@@ -18,11 +51,12 @@ def test_backstage_prints_only_off_stage(monkeypatch, capsys):
|
||||
def test_backstage_md_renders_only_off_stage(monkeypatch, capsys):
|
||||
# Off stage it must emit SOMETHING (rich markdown under a kernel;
|
||||
# IPython's display degrades to print under plain pytest) …
|
||||
monkeypatch.delenv("MERCURY_CONFIG_DIR", raising=False)
|
||||
staging.backstage_md("**visible**")
|
||||
assert capsys.readouterr().out != ""
|
||||
|
||||
# … and on stage, nothing at all.
|
||||
monkeypatch.setenv("MERCURY_CONFIG_DIR", "/tmp/app")
|
||||
# … and on stage — either signal — nothing at all.
|
||||
monkeypatch.setenv(
|
||||
"JPY_SESSION_NAME", "notebooks/cx_discovery__mercury__ab12cd34.ipynb"
|
||||
)
|
||||
staging.backstage_md("**hidden**")
|
||||
assert capsys.readouterr().out == ""
|
||||
|
||||
Reference in New Issue
Block a user