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

@@ -200,14 +200,22 @@ in [`template/MercuryNotebook/notebooks/business_case.ipynb`](../template/Mercur
### 6 · Stage / backstage
The Mercury app is **the stage** — stakeholder-facing. JupyterLab and nbconvert are
**backstage** — analyst diagnostics and the data appendix belong there. Detection:
the `mercury` CLI exports `MERCURY_CONFIG_DIR` into its server and every kernel
inherits it; JupyterLab/nbconvert kernels don't have it.
**backstage** — analyst diagnostics and the data appendix belong there. Detection
(corrected in v1.1.0 — the old env-var-only check leaked backstage content on real
serves): 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` — a per-view signal that works however mercury was
launched, even on one hybrid server. `MERCURY_CONFIG_DIR` is only a fallback: the
CLI exports it **only when `--working-dir` is passed**, it is server-level (a
JupyterLab view on that same server inherits it too — diagnostics then hidden,
never leaked), and it is absent entirely on a plain `mercury` launch.
```python
# studylib/staging.py — copy verbatim
# staging.py — copy verbatim (canonical: the CX Discovery reference)
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
def backstage(*args, **kwargs) -> None:
@@ -432,11 +440,13 @@ Each of these cost a debugging session or a client-facing embarrassment. Don't.
[theme] # NTT DATA brand palette — see docs/brand.md
```
- `MERCURY_CONFIG_DIR` — set by the `mercury` CLI for its server; kernels inherit it.
The pattern uses its **presence** as the stage signal (`studylib/staging.py`). Do not
set it manually except to simulate the stage in tests.
- Serve from the project root: `mercury --working-dir notebooks/` (so `config.toml`
loads). Analyst view: `jupyter lab`. Exports: `python scripts/export_report.py`.
- `MERCURY_CONFIG_DIR` — set by the `mercury` CLI **only when `--working-dir` is
passed**; kernels inherit it. It is the *fallback* stage signal; the primary is
the `__mercury__` shadow-session name (Required §6). Do not set either manually
except to simulate the stage in tests.
- Serve from the study root: `mercury --working-dir .` (so `config.toml` loads and
the env fallback is armed). Analyst view: a **separate** `jupyter lab`. Exports:
`python scripts/export_report.py`.
---