diff --git a/apps/api/tests/unit/deployments/test_beamline_descriptor.py b/apps/api/tests/unit/deployments/test_beamline_descriptor.py index 52067a6f88e..90bd2e2bacb 100644 --- a/apps/api/tests/unit/deployments/test_beamline_descriptor.py +++ b/apps/api/tests/unit/deployments/test_beamline_descriptor.py @@ -210,112 +210,22 @@ def _render_all_pages(slug: str) -> dict[str, str]: slug=slug, catalog_families=frozenset(f.name for f in catalog.families), catalog_models=frozenset(m.name for m in catalog.models), - model_tier=descriptor.beamline.deployment_tier == "model", ) -_SYNTHETIC_STAGES_MODEL = ( - "beamline:\n" - " name: Synthetic\n" - " maturity: model\n" - " evidence: controls_config\n" - " coverage: full\n" - " page_layout: stages\n" - " shape: A synthetic stages-layout model beamline, for pinning the generator.\n" - "source:\n" - " stage: source\n" - " devices:\n" - " - {name: SynMotor, family: LinearStage, pv: 'syn:m1'}\n" - "sample:\n" - " stage: sample\n" - " devices: []\n" - "detector:\n" - " stage: detection\n" - " devices: []\n" -) - - -def test_stages_layout_dissolves_inventory_into_flat_stage_pages(tmp_path: Path) -> None: - # A model-tier beamline with page_layout: stages dissolves Inventory into - # flat source/sample/detector/controls siblings, with no equipment/ folder - # and no beamline.md or inventory.md. Pinned with a synthetic descriptor - # (the real fleet exercising this layout now lives in xmap/descriptors). - path = tmp_path / "beamline.yaml" - path.write_text(_SYNTHETIC_STAGES_MODEL, encoding="utf-8") - descriptor = bd.load(path) - pages = bp.render_all(descriptor, slug="syn", model_tier=True) - assert "deployments/syn/source.md" in pages - assert "deployments/syn/sample.md" in pages - assert "deployments/syn/detector.md" in pages - assert "deployments/syn/controls.md" in pages - assert "deployments/syn/beamline.md" not in pages - assert "deployments/syn/inventory.md" not in pages - assert not any(p.startswith("deployments/syn/equipment/") for p in pages) - # the flat source page is the source stage itself, with no Inventory pointer - # and none of the walk-layout framing (no "walk", no composed-fixture pages, - # and no dangling Operations reference, which is not a page in this layout) - source = pages["deployments/syn/source.md"] - assert source.startswith("# Source") - assert "inventory.md" not in source - assert "walk" not in source.lower() - assert "composed-fixture" not in source - assert "Operations" not in source - assert "[Controls](controls.md)" in source - # Enclosures are a beamline-wide fact: the table moves up to the index and - # off the Source page in the stages layout. - assert "## Enclosures" not in source - # the index presents the stages as first-class sibling pages, not a "Walk the - # beam" spine, and no longer points at Inventory - index = pages["deployments/syn/index.md"] - assert "[Source](source.md)" in index - assert "Walk the beam" not in index - assert "## The beamline" in index - assert "inventory.md" not in index - assert "equipment/" not in index - - def test_pilot_stages_layout_generates_only_flat_source() -> None: - # A pilot on the stages layout generates ONLY its Source page (flat source.md); - # its index, sample, detector, controls, and operational pages are hand-authored. + # 2-BM (the only deployment cora carries) generates ONLY its Source page + # (flat source.md); its index, sample, detector, controls, and operational + # pages are hand-authored. pages = _render_all_pages("2-bm") assert set(pages) == {"deployments/2-bm/source.md"}, sorted(pages) assert pages["deployments/2-bm/source.md"].startswith("# Source") -_SYNTHETIC_SOURCE_REF_MODEL = ( - "beamline:\n" - " name: Synthetic\n" - " maturity: model\n" - " evidence: controls_config\n" - " coverage: full\n" - " page_layout: stages\n" - " shape: A synthetic stages-layout model beamline with a source_ref.\n" - " source_ref:\n" - ' label: "org/repo"\n' - ' url: "https://example.test/org/repo"\n' - "source:\n" - " stage: source\n" - " devices: []\n" -) - - -def test_source_ref_renders_as_provenance_link_on_index_not_source(tmp_path: Path) -> None: - # A model-tier beamline with a source_ref surfaces it in the index's - # generated-from banner as a link, so a reader can trace the facts to the - # public source they came from, and does NOT duplicate it on the Source - # page (that placement is reserved for a pilot, see the test below). - path = tmp_path / "beamline.yaml" - path.write_text(_SYNTHETIC_SOURCE_REF_MODEL, encoding="utf-8") - descriptor = bd.load(path) - pages = bp.render_all(descriptor, slug="syn", model_tier=True) - assert "[org/repo](https://example.test/org/repo)" in pages["deployments/syn/index.md"] - assert "Source: [" not in pages["deployments/syn/source.md"] - - def test_pilot_source_ref_surfaces_on_source_page(tmp_path: Path) -> None: - # A pilot has no generated index, so its Source page is the only place a - # source_ref can land. No real pilot currently carries a source_ref (2-BM - # is live), so render a synthetic pilot to pin the path. + # The Source page is where a source_ref lands, if a descriptor carries one. + # No real descriptor currently does (2-BM is live), so render a synthetic + # one to pin the path. descriptor_yaml = ( "beamline:\n" " name: T\n" @@ -334,7 +244,7 @@ def test_pilot_source_ref_surfaces_on_source_page(tmp_path: Path) -> None: path = tmp_path / "beamline.yaml" path.write_text(descriptor_yaml, encoding="utf-8") descriptor = bd.load(path) - pages = bp.render_all(descriptor, slug="t", model_tier=False) + pages = bp.render_all(descriptor, slug="t") assert "[org/repo](https://example.test/org/repo)" in pages["deployments/t/source.md"] @@ -348,36 +258,6 @@ def test_live_pilot_has_no_source_ref() -> None: assert "Source: [" not in source_page -def test_walk_layout_keeps_beamline_and_inventory_pages(tmp_path: Path) -> None: - # The walk layout (page_layout omitted -> "walk") still emits beamline.md, - # inventory.md, and the equipment/ stage pages with a "Walk the beam" spine. - # 2-BM (the only real deployment left) uses the stages layout, so this pins - # the walk path with a synthetic model-tier descriptor. - descriptor_yaml = ( - "beamline:\n" - " name: W\n" - " maturity: model\n" - " evidence: controls_config\n" - " coverage: full\n" - "source:\n stage: source\n devices: []\n" - "sample:\n stage: sample\n devices: []\n" - "detector:\n stage: detection\n devices: []\n" - ) - path = tmp_path / "beamline.yaml" - path.write_text(descriptor_yaml, encoding="utf-8") - descriptor = bd.load(path) - pages = bp.render_all(descriptor, slug="w", model_tier=True) - assert "deployments/w/beamline.md" in pages - assert "deployments/w/inventory.md" in pages - assert "deployments/w/equipment/sample.md" in pages - assert "deployments/w/equipment/detector.md" in pages - assert "deployments/w/equipment/controls.md" in pages - # the walk layout keeps the "Walk the beam" spine and its Inventory pointer - index = pages["deployments/w/index.md"] - assert "## Walk the beam" in index - assert "[Inventory](inventory.md)" in index - - def test_markers_promoted_from_comments_to_fields() -> None: descriptor = bd.load(_DESCRIPTOR) devices = {d.name: d for _name, group in descriptor.groups for d in group.devices} @@ -562,7 +442,6 @@ def _generated_beamline_pages(deployment: str) -> str: slug=deployment, catalog_families=frozenset(f.name for f in catalog.families), catalog_models=frozenset(m.name for m in catalog.models), - model_tier=descriptor.beamline.deployment_tier == "model", ) return "\n".join(pages.values()) diff --git a/deployments/README.md b/deployments/README.md new file mode 100644 index 00000000000..f81fe0fdc52 --- /dev/null +++ b/deployments/README.md @@ -0,0 +1,5 @@ +# Deployments + +The descriptors for CORA's real deployments: `/beamline.yaml` per beamline, `/site.yaml` per facility. The docs build renders these directly (`scripts/beamline_descriptor.py`, `scripts/site_descriptor.py`, `scripts/mkdocs_hooks.py`), and a handful of `apps/api` fitness tests validate against them. + +Two directories today: `2-bm` (CORA's one real, operational deployment) and `aps` (the Facility record 2-BM belongs to). That is deliberate, not partial: CORA models further beamlines, at APS and elsewhere, from public source, to test that the domain model generalizes, but a beamline CORA has no operational relationship with is not a deployment. That corpus lives in the private [`xmap/descriptors`](https://github.com/xmap/descriptors) repo instead, kept out of both this directory and the public docs site. diff --git a/deployments/aps/site.yaml b/deployments/aps/site.yaml index 36b3d8b2426..65c8d03393f 100644 --- a/deployments/aps/site.yaml +++ b/deployments/aps/site.yaml @@ -23,10 +23,9 @@ facility: kind: Site institution: Argonne control_plane: "EPICS / ophyd" - beamlines_note: "The one Site where CORA spans all three maturities: 2-BM runs live, 7-BM / 19-BM / 32-ID are in design, and the rest are reverse-engineered models, all sharing the APS storage-ring envelope." - beamlines_note: "The pilot 2-BM runs live; 7-BM, 19-BM, and 32-ID are in design; the Sector 4/8/9/12/13 beamlines are reverse-engineered models. All share the APS storage-ring envelope." + beamlines_note: "CORA has also modeled further APS beamlines from public source, to test that the domain model generalizes; those are not deployments and live in the private xmap/descriptors repo, not here." sectors: [Sector 2, Sector 4, Sector 7, Sector 8, Sector 9, Sector 12, Sector 13, Sector 32, Sector 19] - beamlines: [2-BM, 2-ID, 7-BM, 32-ID, 19-BM, 4-ID, 8-ID, 9-ID, 12-ID, 13-ID] + beamlines: [2-BM] # ISA-88 Site Recipes: the facility-adapted form of a catalog Method. A method # that exists in the Catalog renders as a link; a method still pending in the diff --git a/scripts/beamline_pages.py b/scripts/beamline_pages.py index 099137bcae5..7b8527d6f2f 100644 --- a/scripts/beamline_pages.py +++ b/scripts/beamline_pages.py @@ -1,15 +1,26 @@ """Render a beamline descriptor into a docs page. `render_all(descriptor)` returns a {src_uri: markdown} dict (mirroring the -contract scripts/scenarios_pages.render_all used) with a single generated page, -deployments/2-bm/beamline.md: the Source page, a walk along the source-stage -devices (front-end optics to the sample), one section per subsystem with a device -table per group. The cross-cutting controllers and supplies are their own pages -(equipment/controls.md, operations.md); the descriptor still carries them for the -tests and the future seeder. - -The mkdocs on_files hook in scripts/mkdocs_hooks.py injects these as virtual -files at build time; nothing is written to disk. +contract scripts/scenarios_pages.render_all used) with a single generated page: +2-BM's Source-stage walk (front-end optics to the sample), one section per +subsystem with a device table per group. 2-BM hand-authors everything else +(index, sample, detector, controls, operations, ...); this is the one page +generated from the descriptor, so it cannot silently drift from what the +source-stage devices actually are. + +This module used to also generate a beamline's whole reader set (index, +inventory, the sample/detector/controls beam-walk) for "model-tier" beamlines: +CORA's further modeled-but-not-deployed beamlines, reverse-engineered from +public source. That corpus moved to the private xmap/descriptors repo, and the +generator went with the reasoning that moved it: cora's own domain model "only +contains what at least one real deployment forced into it" (docs/deployments/ +index.md), and the same now holds for this generator. 2-BM is the only +descriptor cora carries, and it is not model-tier, so that code had no real +input left to run against. Recoverable from git history if a second real +deployment ever needs it. + +The mkdocs on_files hook in scripts/mkdocs_hooks.py injects this as a virtual +file at build time; nothing is written to disk. """ from __future__ import annotations @@ -71,24 +82,13 @@ def render_all( slug: str = "2-bm", catalog_families: frozenset[str] = frozenset(), catalog_models: frozenset[str] = frozenset(), - facility_label: str | None = None, - control_plane: str | None = None, - model_tier: bool = False, ) -> dict[str, str]: - """Render a beamline's generated docs pages. - - Every beamline gets the Source-stage walk (`beamline.md`). A model-tier - beamline (a reverse-engineered / design scaffold, not one of the richly - hand-authored pilots) additionally gets its whole reader set generated from - the descriptor: the front-door `index.md`, the `inventory.md` reference, and - the Sample / Detector / Controls beam-walk pages. The pilots pass - model_tier=False and keep their hand-authored set; only their Source walk is - generated, as before. - - A model-tier beamline with `page_layout: stages` (the SRX pilot) uses a - flattened set instead: Inventory is dissolved into flat `source.md`, - `sample.md`, `detector.md`, and `controls.md` siblings (no `equipment/` - folder, no `inventory.md`), with `index.md` linking them directly. + """Render the beamline's one generated page: its Source-stage walk. + + `page_layout: stages` (2-BM's layout) generates a flat `source.md` sibling + of the hand-authored pages; `page_layout: walk` generates `beamline.md` + instead, with the walk-layout framing in its intro (no current descriptor + uses it, but nothing here assumes stages is the only valid choice). """ global _KNOWN_FAMILIES, _KNOWN_MODELS _KNOWN_FAMILIES = catalog_families @@ -97,50 +97,22 @@ def render_all( layout = descriptor.beamline.page_layout if layout == "stages": - # Stages layout: the generated Source page sits flat at source.md. A - # model-tier beamline's whole reader set is generated (index + the flat - # stage pages); a pilot hand-authors index / sample / detector / controls - # and their rich operational pages, so only its Source page generates. - pages = { + return { f"deployments/{slug}/source.md": _render_page( descriptor, slug=slug, blob_url=blob_url, link_inventory=False, - include_enclosures=not model_tier, flat=True, - show_source_ref=not model_tier, + show_source_ref=True, ), } - if model_tier: - pages[f"deployments/{slug}/index.md"] = _render_index( - descriptor, - slug=slug, - facility_label=facility_label, - control_plane=control_plane, - page_layout=layout, - ) - pages.update( - _render_beamwalk( - descriptor, slug=slug, control_plane=control_plane, prefix="", depth="../../" - ) - ) - return pages - - pages = { + + return { f"deployments/{slug}/beamline.md": _render_page( - descriptor, slug=slug, blob_url=blob_url, show_source_ref=not model_tier + descriptor, slug=slug, blob_url=blob_url, show_source_ref=True ) } - if model_tier: - pages[f"deployments/{slug}/index.md"] = _render_index( - descriptor, slug=slug, facility_label=facility_label, control_plane=control_plane - ) - pages[f"deployments/{slug}/inventory.md"] = _render_inventory( - descriptor, slug=slug, blob_url=blob_url - ) - pages.update(_render_beamwalk(descriptor, slug=slug, control_plane=control_plane)) - return pages def _esc(text: str) -> str: @@ -418,254 +390,3 @@ def _render_page( blocks.append(_render_group(name, group)) return "\n\n".join(blocks) + "\n" - - -# --------------------------------------------------------------------------- -# Model-tier reader pages, generated from the descriptor. -# -# A model beamline's whole reader set is generated so it cannot drift or carry -# engineering-internal bookkeeping (rule-of-three, loose-family graduation, -# tracking tags). The set is beamline-natural: a front door, then the beam walk -# (source to detector) plus controls. The default "walk" layout keeps the full -# asset tree as a separate Inventory reference; the "stages" layout dissolves -# Inventory into the flat source/sample/detector/controls stage pages. -# --------------------------------------------------------------------------- - -_STAGE_FILE = {"sample": "sample.md", "detection": "detector.md"} -_STAGE_TITLE = {"sample": "Sample", "detection": "Detector"} - - -def _confirm_clause(descriptor: BeamlineDescriptor) -> str: - beamline = descriptor.beamline - if beamline.evidence == "live": - return "" - if beamline.evidence == "controls_config": - return ( - " The device handles are read from the facility's public controls configuration and " - "verified against it; vendor parts, energies, and physical positions are not in it and " - "are carried `confirm` until beamline staff verify them." - ) - if beamline.evidence == "design_report": - return ( - " The values are read from the beamline's design report and carried `confirm` until " - "staff verify them against the built instrument." - ) - return ( - " The device families are inferred from public facility pages and papers; no control " - "handles or vendor models are public, so every value is carried `confirm` until staff " - "verify it." - ) - - -def _render_index( - descriptor: BeamlineDescriptor, - *, - slug: str, - facility_label: str | None, - control_plane: str | None, - page_layout: str = "walk", -) -> str: - beamline = descriptor.beamline - extra = beamline.model_extra or {} - name = beamline.name or slug - summary = beamline.summary or "" - blocks: list[str] = [f"# {name}"] - if summary: - cov = " A deliberately partial first cut." if beamline.coverage == "partial" else "" - blocks.append(f"*{summary}.{cov}*") - - # Facts table: identity + seam, all from the descriptor. - facility_cell = beamline.facility or "" - if facility_label: - facility_cell = f"[{facility_label}](../{beamline.facility}/index.md)" - if control_plane: - facility_cell += f" ({control_plane})" - facts: list[list[str]] = [["Facility", facility_cell]] - if beamline.sector: - facts.append(["Sector", str(beamline.sector)]) - src = str(beamline.source) if beamline.source else "" - if src: - facts.append(["Source", src]) - facts.append( - ["Modelled", f"{_MATURITY[beamline.maturity]}, {_COVERAGE[beamline.coverage]} coverage"] - ) - blocks.append(_table(["Property", "Value"], facts)) - - # The provenance caveat (how trustworthy the facts are, keyed on evidence - # tier) folds into the generated-from banner rather than a near-identical - # "What CORA models" section; the enclosures it used to name are in the - # table below. - banner = ( - f"This page is generated from the descriptor at " - f"[`deployments/{slug}/beamline.yaml`]({_BLOB_BASE}/deployments/{slug}/beamline.yaml). " - "Edit the descriptor, not this page." - ) - caveat = _confirm_clause(descriptor).strip() - ref = beamline.source_ref - if ref is not None: - # Name the specific public source the facts were read from, alongside the - # evidence-tier caveat. - caveat = (caveat + " ").lstrip() + f"Source: [{ref.label}]({ref.url})." - if caveat: - banner += "\n\n" + caveat - blocks.append(_admonition(banner, kind="info", title="Generated from the descriptor")) - - stages = {g.stage for _n, g in descriptor.groups} - flat = page_layout == "stages" - - # Enclosures are a beamline-wide spatial fact (every stage sits in a hutch), - # so the stages layout carries the table on the index rather than the Source - # page. The walk layout keeps it on the Source page (see _render_page). - if flat: - blocks.extend(_enclosures_blocks(descriptor)) - - if flat: - # Stages layout: the stages are first-class sibling pages, so present - # them as a section list (in beam order, controls last) rather than a - # one-line walk sentence. No Inventory (the stage pages are the tree). - # The optional one-line shape leads the section when authored. - lead = beamline.shape.strip() if beamline.shape else "The devices along the beam, area by area." - section = ["## The beamline", lead] - bullets = ["- [Source](source.md): the beam, produced and conditioned before the sample."] - if "sample" in stages: - bullets.append("- [Sample](sample.md): the sample environment and its positioning.") - if "detection" in stages: - bullets.append("- [Detector](detector.md): what records the beam after the sample.") - bullets.append("- [Controls](controls.md): the control plane CORA's edge conducts over.") - section.append("\n".join(bullets)) - blocks.append("\n\n".join(section)) - else: - # Walk layout: the beam walk as a one-line spine, linking the equipment/ - # pages and pointing at the Inventory reference for the full device tree. - walk_links = ["[Source](beamline.md)"] - if "sample" in stages: - walk_links.append("[Sample](equipment/sample.md)") - if "detection" in stages: - walk_links.append("[Detector](equipment/detector.md)") - walk_links.append("[Controls](equipment/controls.md)") - walk_sentence = ( - "The instrument, area by area along the beam: " - + " to ".join(walk_links[:-1]) - + f", driven by {walk_links[-1]}." - + " The full device tree, with families and control handles, is the " - "[Inventory](inventory.md)." - ) - blocks.append("## Walk the beam\n\n" + walk_sentence) - - # Every non-pilot beamline's model page closes by pointing here to explain - # why it has no runbook and no live experiment view. The section was lost - # when these index pages became generated, orphaning that link on 56 pages. - if beamline.maturity != "pilot": - blocks.append("## Not yet documented") - blocks.append( - "This beamline has no Operations runbook and no live Experiment view, " - "because CORA does not drive it yet. Both pages describe what an operator " - "does with a running system, so writing them for a beamline CORA only " - "models would be invention rather than documentation. The pilot at " - "[2-BM](../2-bm/index.md) carries both, and shows the shape they take " - "once a deployment goes live." - ) - - blocks.append("## More") - blocks.append( - "- [Notes](notes.md): techniques, governance, the CORA model index, and " - "open modelling questions." - ) - return "\n\n".join(blocks) + "\n" - - -_MATURITY = {"pilot": "operational pilot", "design": "in design", "model": "reverse-engineered"} -_COVERAGE = {"full": "full", "partial": "partial"} - - -def _render_inventory(descriptor: BeamlineDescriptor, *, slug: str, blob_url: str) -> str: - _set_catalog_depth("../../") # deployments//inventory.md - beamline = descriptor.beamline - name = beamline.name or slug - blocks: list[str] = ["# Inventory"] - blocks.append( - f"*The CORA Asset model for the operational core of {name}: every device by beam-path " - "stage, its Family and control handle, and what still needs confirming.*" - ) - blocks.append( - _admonition( - f"Generated from [`deployments/{slug}/beamline.yaml`]({blob_url}). " - "Edit the descriptor, not this page.", - kind="info", - title="Generated from the descriptor", - ) - ) - if descriptor.enclosures: - rows = [ - [ - f"`{e.name}`", - e.role or "", - f"`{e.facility_code}`" if e.facility_code else "", - _permit_signal_cell(e.permit_signal), - ] - for e in descriptor.enclosures - ] - blocks.append("## Enclosures") - blocks.append(_table(["Enclosure", "Role", "Facility", "Permit signal"], rows)) - - # All device groups, in beam-path order, each as its device table. - for stage in ("source", "sample", "detection"): - for gname, group in descriptor.groups: - if group.stage == stage: - blocks.append(_render_group(gname, group)) - if descriptor.controls is not None: - controls_devices = [ - *descriptor.controls.motion_controllers, - *descriptor.controls.triggering, - ] - if controls_devices: - blocks.append("## Controls") - if descriptor.controls.intro: - blocks.append(descriptor.controls.intro.strip()) - blocks.append(_device_table(controls_devices)) - return "\n\n".join(blocks) + "\n" - - -def _render_beamwalk( - descriptor: BeamlineDescriptor, - *, - slug: str, - control_plane: str | None, - prefix: str = "equipment/", - depth: str = "../../../", -) -> dict[str, str]: - # `prefix`/`depth` place the stage pages: the default "equipment/" + - # "../../../" is the walk layout; the stages layout passes "" + "../../" so - # the pages are flat siblings of index.md. - pages: dict[str, str] = {} - _set_catalog_depth(depth) - # Sample + Detector: one page per stage, its groups rendered. - for stage, filename in _STAGE_FILE.items(): - groups = [(n, g) for n, g in descriptor.groups if g.stage == stage] - if not groups: - continue - blocks = [f"# {_STAGE_TITLE[stage]}"] - # A single group whose name is just the stage would render a `##` heading - # duplicating the page title; render its body inline instead. - if len(groups) == 1 and _humanize(groups[0][0]) == _STAGE_TITLE[stage]: - blocks.append(_render_group_body(groups[0][1])) - else: - for gname, group in groups: - blocks.append(_render_group(gname, group)) - pages[f"deployments/{slug}/{prefix}{filename}"] = "\n\n".join(blocks) + "\n" - - # Controls: the cross-cutting drive electronics + the seam sentence. - controls = descriptor.controls - cblocks = ["# Controls"] - seam = "The control plane the beamline runs on, and the seam CORA's edge conducts over." - if control_plane: - seam += f" Control plane: {control_plane}." - cblocks.append(seam) - if controls is not None: - if controls.intro: - cblocks.append(controls.intro.strip()) - devices = [*controls.motion_controllers, *controls.triggering] - if devices: - cblocks.append(_device_table(devices)) - pages[f"deployments/{slug}/{prefix}controls.md"] = "\n\n".join(cblocks) + "\n" - return pages diff --git a/scripts/mkdocs_hooks.py b/scripts/mkdocs_hooks.py index 4705b954a84..dace7e7e2ed 100644 --- a/scripts/mkdocs_hooks.py +++ b/scripts/mkdocs_hooks.py @@ -261,14 +261,6 @@ def on_files(files: Any, *, config: Any) -> Any: generated: dict[str, str] = {} - # Load every site first so a beamline render can reach its Site's facility - # label + control-plane seam (the generated model-tier index / controls - # pages echo them). code -> (heading, control_plane). - site_by_code: dict[str, tuple[str, str | None]] = {} - for path in sorted(DEPLOYMENTS_DIR.glob("*/site.yaml")): - f = site_descriptor.load(path).facility - site_by_code[f.code] = (f.heading or f.code.upper(), f.control_plane) - # Beamlines: render each, and build the facility -> [BeamlineRef] map the # site pages use for their roster (label, slug, badges, summary), so the # facility page reads the one-liner + badges from the descriptor, not a @@ -278,16 +270,12 @@ def on_files(files: Any, *, config: Any) -> Any: slug = path.parent.name descriptor = beamline_descriptor.load(path) b = descriptor.beamline - facility_label, control_plane = site_by_code.get(b.facility or "", (None, None)) generated.update( beamline_pages.render_all( descriptor, slug=slug, catalog_families=catalog_families, catalog_models=catalog_models, - facility_label=facility_label, - control_plane=control_plane, - model_tier=b.deployment_tier == "model", ) ) if b.facility: