diff --git a/apps/api/tests/unit/deployments/test_site_descriptor.py b/apps/api/tests/unit/deployments/test_site_descriptor.py index d34d8e1082..a1fd4fa7ea 100644 --- a/apps/api/tests/unit/deployments/test_site_descriptor.py +++ b/apps/api/tests/unit/deployments/test_site_descriptor.py @@ -202,6 +202,11 @@ def test_renders_single_site_narrative() -> None: assert "beam-flux transients" in page # cautions assert "Institution" in page and "Argonne" in page # facility -> institution (context) assert "../argonne/index.md" not in page # institution is context, not a navigable deployment + # the Asset binding is dissolved into this page, not a separate Assets sub-page + assert "## How APS is modeled" in page + assert "../2-bm/index.md" in page # beamline root Asset binding, folded in + assert "`Unit`" in page # asset tier column rendered inline + assert "assets.md" not in page # no link-out to a hand-authored Assets page def test_practice_method_links_only_known() -> None: diff --git a/docs/deployments/aps/assets.md b/docs/deployments/aps/assets.md deleted file mode 100644 index f722f6a8d2..0000000000 --- a/docs/deployments/aps/assets.md +++ /dev/null @@ -1,11 +0,0 @@ -# Assets - -*Equipment BC Assets bound to the APS Site.* - -APS itself is not an Asset: it is a Federation `Facility` with `FacilityKind = Site` (`FacilityCode = "aps"`), declared on the [APS index](index.md). Sectors are facility-envelope scope, an organizational grouping rather than Asset rows; if modeled they are a `Facility` with `FacilityKind = Area` under the Site. In the pilot, beamlines are root Assets (`tier = Unit`, `parent_id = None`) that bind the Site directly via `facility_code`. See [Model](../../architecture/model.md) for the aggregate shape. - -| Asset | Tier | facility_code | Hosts | -| --- | --- | --- | --- | -| `2-BM` | `Unit` | `aps` | [2-BM](../2-bm/index.md) | - -Sub-systems and devices nested under a beamline are Assets with `tier = Component` or `tier = Device`, linked via `parent_id`. Being non-root, they do not carry `facility_code`; they inherit facility scope through the `parent_id` tree. diff --git a/docs/deployments/index.md b/docs/deployments/index.md index b3273abe67..23591797b5 100644 --- a/docs/deployments/index.md +++ b/docs/deployments/index.md @@ -4,33 +4,23 @@ A deployment is a beamline pilot: one instrument where the recipe ladder, BCs, and trust boundaries meet real users. Vertical before horizontal. CORA's domain model only contains what at least one real deployment forced into it; until a beamline demands a shape, the shape stays out. -CORA's operational pilot today is 2-BM, a bending-magnet micro-CT beamline at APS. A second deployment, TomoWise at MAX IV, is in the design phase: its beamline is modelled from the Technical Design Report ahead of construction, so its pages describe an intended shape, not a running instrument. These pages are framed from the beamline outward: the beamline first, then the site it runs at. +CORA's operational pilot today is 2-BM, a bending-magnet micro-CT beamline at APS. A second deployment, TomoWise at MAX IV, is in the design phase: its beamline is modelled from the Technical Design Report ahead of construction, so its pages describe an intended shape, not a running instrument. These pages are framed from the beamline outward: the beamline first, then the facility it runs at. | Beamline | Site | Status | | --- | --- | --- | | [2-BM](2-bm/index.md) | [APS](aps/index.md), Argonne | Pilot | | [TomoWise](tomowise/index.md) | [MAX IV](maxiv/index.md), Lund | In design | -## The facility envelope +## The facilities -A beamline is never standalone: it sits inside a facility envelope, the Site that operates it and the institution above that. That context is what a beamline points up into for its clearances, principals, practices, and facility-scope supplies. The envelope is not a separate deployment in its own right, so it lives here as context for the beamline rather than as a peer entry above. +A beamline is never standalone: it sits inside a facility, the Site that operates it and the institution above that. The facility is what a beamline points up into for the clearances, principals, practices, and facility-scope supplies it inherits but does not own. CORA carries one page per Site, [APS](aps/index.md) and [MAX IV](maxiv/index.md), and each beamline links up to its own rather than restating it. -CORA models the three scope levels with three different mechanisms. Facility-envelope scope (institution, site, area) is owned by the Federation `Facility` aggregate, whose `FacilityKind` is `{Site, Area}`. Equipment scope is owned by the `Asset` aggregate, whose `tier` is the closed `AssetTier` StrEnum `{Unit, Component, Device}` (ISA-88 equipment tiers). A root Asset binds its owning Facility through `facility_code`; nested Assets inherit that scope through `parent_id`. +Facility scope and equipment scope are two different aggregates. A Site, and a sector or hutch grouping below it, is a Federation `Facility`, whose `FacilityKind` is `{Site, Area}`. A beamline and the sub-systems under it are Equipment `Asset` rows, whose `tier` is the closed `AssetTier` StrEnum `{Unit, Component, Device}` (ISA-88 equipment tiers). A beamline is a root Asset bound to its Site through `facility_code`; its sub-systems inherit that scope through `parent_id`. Each facility page carries that binding for the Site it describes. -| Scope level | Example | Model | -| --- | --- | --- | -| Institution | Argonne; Lund University | Context, not a registered row | -| Site | [APS](aps/index.md); [MAX IV](maxiv/index.md) | Federation `Facility`, `FacilityKind = Site` (`facility_code = "aps"` / `"maxiv"`) | -| Beamline | [2-BM](2-bm/index.md) at APS; [TomoWise](tomowise/index.md) at MAX IV | Equipment `Asset`, root, `tier = Unit`, bound by `facility_code` | - -## The sites they run at +[**APS**](aps/index.md), the Advanced Photon Source, operated by Argonne National Laboratory, is the synchrotron site 2-BM runs at. Its page is the home for the facts a 2-BM experiment inherits but does not own: the Practices (ISA-88 Site Recipes) it runs, the Clearances it must hold, the facility Supplies it draws on, and the people and agents registered facility-wide. -[**APS**](aps/index.md), the Advanced Photon Source, is the synchrotron site 2-BM runs at, operated by Argonne National Laboratory. It is a Federation `Facility` with `FacilityKind = Site` (`facility_code = "aps"`). The APS page is the home for facts the beamline inherits but does not own: the Practices (ISA-88 Site Recipes) it runs, the Clearances it must hold, the facility Supplies it draws on, and the people and agents registered facility-wide. The 2-BM page links up to these rather than restating them. +[**MAX IV**](maxiv/index.md), in Lund, Sweden, is the synchrotron site the planned TomoWise beamline will run at, the second Site CORA models. Its page is thin while TomoWise is in design: most facility facts (safety forms, supplies, the operator pool) are carried pending until MAX IV staff confirm them. -[**MAX IV**](maxiv/index.md), in Lund, Sweden, is the synchrotron site the planned TomoWise beamline will run at, the second Site CORA models. It is a Federation `Facility` with `FacilityKind = Site` (`facility_code = "maxiv"`). Its page is thin while TomoWise is in design: most facility facts (safety forms, supplies, the operator pool) are carried pending until MAX IV staff confirm them. - -The operating institutions, Argonne and Lund University, are context, not modeled rows: there is no Enterprise or Institution kind. A sector or hutch grouping is a `Facility` with `FacilityKind = Area` only if it ever needs modeling, never an Asset row. +The operating institutions, Argonne and Lund University, are context, not modeled rows: there is no Enterprise or Institution kind. Cross-facility vocabulary (Capabilities, Methods) lives in the [Catalog](../catalog/index.md), since it is not bound to any single Site. - -A second Site is what graduates the facility envelope from a single appendix into its own area: the facility pages now carry one page per Site, [APS](aps/index.md) and [MAX IV](maxiv/index.md), with each beamline linking up to its own. diff --git a/docs/deployments/maxiv/assets.md b/docs/deployments/maxiv/assets.md deleted file mode 100644 index 2625742150..0000000000 --- a/docs/deployments/maxiv/assets.md +++ /dev/null @@ -1,13 +0,0 @@ -# Assets - -*Equipment BC Assets bound to the MAX IV Site.* - -MAX IV itself is not an Asset: it is a Federation `Facility` with `FacilityKind = Site` (`FacilityCode = "maxiv"`), declared on the [MAX IV index](index.md). In the pilot pattern, beamlines are root Assets (`tier = Unit`, `parent_id = None`) that bind the Site directly via `facility_code`. See [Model](../../architecture/model.md) for the aggregate shape. - -| Asset | Tier | facility_code | Hosts | -| --- | --- | --- | --- | -| `TomoWISE` | `Unit` | `maxiv` | [TomoWise](../tomowise/index.md) | - -Sub-systems and devices nested under the beamline are Assets with `tier = Component` or `tier = Device`, linked via `parent_id`. Being non-root, they do not carry `facility_code`; they inherit facility scope through the `parent_id` tree. - -TomoWISE is in the design phase: the asset tree above is the planned shape, not a registered inventory. diff --git a/mkdocs.yml b/mkdocs.yml index 14473e0ad1..4ae3a52269 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -120,13 +120,9 @@ nav: - Techniques: deployments/tomowise/techniques.md - Governance: deployments/tomowise/governance.md - Model: deployments/tomowise/model.md - - Facility envelope: - - APS: - - deployments/aps/index.md - - Assets: deployments/aps/assets.md - - MAX IV: - - deployments/maxiv/index.md - - Assets: deployments/maxiv/assets.md + - Facilities: + - APS: deployments/aps/index.md + - MAX IV: deployments/maxiv/index.md - Catalog: - catalog/index.md - Capabilities: catalog/capabilities.md diff --git a/scripts/site_pages.py b/scripts/site_pages.py index 05556426bb..b35920ce8e 100644 --- a/scripts/site_pages.py +++ b/scripts/site_pages.py @@ -177,15 +177,28 @@ def _principals(site: Site, *, site_label: str) -> list[str]: return blocks -def _modeled(site_label: str) -> list[str]: - return [ +def _modeled( + site_label: str, *, facility_code: str, beamlines: list[tuple[str, str]] +) -> list[str]: + blocks = [ f"## How {site_label} is modeled", f"{site_label} itself is not an Asset: it is a Federation `Facility` with " - "`FacilityKind = Site`. The beamlines it hosts are the root Assets, each bound to the Site " - "by `facility_code`; their sub-systems nest below by `parent_id`. See " - f"[Assets](assets.md) for that binding and [the CORA model]({MODEL_PAGE}) for the " - "aggregate shapes.", + f'`FacilityKind = Site` (`facility_code = "{facility_code}"`). The beamlines it hosts are ' + "the root Assets (`tier = Unit`, `parent_id = None`), each bound to the Site directly by " + f"`facility_code`. See [the CORA model]({MODEL_PAGE}) for the aggregate shapes.", ] + if beamlines: + rows = [ + [f"`{label}`", "`Unit`", f"`{facility_code}`", f"[{label}](../{bslug}/index.md)"] + for label, bslug in beamlines + ] + blocks.append(_table(["Asset", "Tier", "facility_code", "Hosts"], rows)) + blocks.append( + "Sub-systems and devices nested under a beamline are Assets with `tier = Component` or " + "`tier = Device`, linked via `parent_id`. Being non-root, they do not carry `facility_code`; " + "they inherit facility scope through the `parent_id` tree." + ) + return blocks def render_all( @@ -208,5 +221,5 @@ def render_all( blocks += _resources(site) blocks += _safety(site) blocks += _principals(site, site_label=site_label) - blocks += _modeled(site_label) + blocks += _modeled(site_label, facility_code=f.code, beamlines=beamlines) return {f"deployments/{slug}/index.md": "\n\n".join(blocks) + "\n"}