///|
/// CompositionView: the curated read-only view of composed capabilities
/// delivered to `Lifecycle::on_compose` after every composition gate has
/// passed. This is the consumption direction of the manifest contract — an
/// extension declares `requires` and the corresponding getter reads `Some`;
/// undeclared capabilities read `None`.
///
/// Curation discipline: the view only ever carries capabilities whose use
/// cannot bypass governance. `model` calls still carry `InvocationScope`
/// attribution; `ui` has no governance surface. The view NEVER exposes tool
/// invocation (would bypass `PipelineHook::before_tool`), session stores (state
/// ownership), or observer-event emission (core-owned). Fields are private
/// with getters so new capabilities can be added without breaking
/// extensions that destructure or construct views in tests.
pub struct CompositionView {
priv model_ : &ModelPort?
priv ui_ : &UiPort?
}
///|
/// The single composed ModelPort — present only when the extension declared
/// `Capability::Model` in its manifest's `requires`. This is the unique
/// outlet that passed the composition cardinality gate, source-agnostic
/// (the extension cannot tell a plain provider from a routing
/// meta-extension, and must not).
pub fn CompositionView::model(self : CompositionView) -> &ModelPort? {
self.model_
}
///|
/// The composed UiPort (single contributor, `CompositeUiPort`, or
/// `NoopUiPort`) — present only when the extension declared
/// `Capability::Ui`. Human-interaction requests (`UiRequest::Confirm` /
/// `Select` / `Input`) go here; do not occupy the `ui` manifest slot just to
/// consume the composed UI.
pub fn CompositionView::ui(self : CompositionView) -> &UiPort? {
self.ui_
}
///|
/// Gate a view on a manifest's declared `requires`: a declared capability is
/// populated from the composed ports, an undeclared one reads `None`.
/// Called by the composition after every gate has passed, so `model` and
/// `ui` are guaranteed to be the final composed values.
pub fn CompositionView::resolve(
requires~ : Array[Capability],
model~ : &ModelPort,
ui~ : &UiPort,
) -> CompositionView {
{
model_: if requires.contains(Model) {
Some(model)
} else {
None
},
ui_: if requires.contains(Ui) {
Some(ui)
} else {
None
},
}
}