feat: add master notebook library scaffolding and review tooling
Add CLAUDE.md defining the Palladium master notebook conventions and Red Panda Approval criteria, plus a review-notebook slash command for LLM-driven notebook review. Expand .gitignore to block client/engagement documents and generated exports, keeping masters client-clean while allowing text/image sources. Normalize slider widget numeric values from floats to integers in notebook JSON.
This commit is contained in:
63
assessments/CX_Discovery_Workshop/tests/conftest.py
Normal file
63
assessments/CX_Discovery_Workshop/tests/conftest.py
Normal file
@@ -0,0 +1,63 @@
|
||||
"""Test plumbing: import path + notebook content served from tagged cells.
|
||||
|
||||
Content and client data live in the notebook, never in ``.py`` — the cells
|
||||
tagged ``topic-bank`` and ``engagement-data`` in
|
||||
``notebooks/cx_discovery.ipynb``. The fixtures below read those cells with
|
||||
nbformat and exec them, so pytest pins the exact content the deliverable
|
||||
ships (no kernel needed — tagged cells are self-contained by contract).
|
||||
|
||||
The sys.path insert makes discoverylib importable even without the study
|
||||
venv active (the normal setup is ``pip install -e ".[dev]"`` into the
|
||||
study-local ``.venv/``).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
|
||||
STUDY_ROOT = pathlib.Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(STUDY_ROOT))
|
||||
|
||||
NOTEBOOK = STUDY_ROOT / "notebooks" / "cx_discovery.ipynb"
|
||||
|
||||
|
||||
def tagged_cell_ns(tag: str) -> dict[str, Any]:
|
||||
"""Exec the single cell carrying ``tag`` and return its namespace."""
|
||||
import nbformat
|
||||
|
||||
nb = nbformat.read(NOTEBOOK, as_version=4)
|
||||
cells = [c for c in nb.cells if tag in c.metadata.get("tags", [])]
|
||||
assert len(cells) == 1, (
|
||||
f"expected exactly one cell tagged {tag!r} in {NOTEBOOK.name}, "
|
||||
f"found {len(cells)}"
|
||||
)
|
||||
ns: dict[str, Any] = {}
|
||||
exec(compile(cells[0].source, f"{NOTEBOOK.name} [{tag}]", "exec"), ns)
|
||||
return ns
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def topic_bank() -> dict[str, Any]:
|
||||
"""The executed namespace of the notebook's topic-bank cell."""
|
||||
return tagged_cell_ns("topic-bank")
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def topics(topic_bank: dict[str, Any]) -> tuple[Any, ...]:
|
||||
"""The TOPICS tuple as the deliverable defines it."""
|
||||
return topic_bank["TOPICS"] # type: ignore[no-any-return]
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def topic_by_key(topics: tuple[Any, ...]) -> dict[str, Any]:
|
||||
return {t.key: t for t in topics}
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def engagement() -> dict[str, Any]:
|
||||
"""The ENGAGEMENT dict as the deliverable's engagement-data cell ships it."""
|
||||
return tagged_cell_ns("engagement-data")["ENGAGEMENT"] # type: ignore[no-any-return]
|
||||
Reference in New Issue
Block a user