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:
2026-07-31 17:30:30 +00:00
parent a967f73d09
commit 22a5d907d6
7 changed files with 3224 additions and 3141 deletions

View File

@@ -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 == ""