///|
/// 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 decision_ : &DecisionPort?
  priv log_ : &LogPort?
  priv ui_ : &UiPort?
  priv tasks_ : Tasks?
  priv wakeup_ : WakeupPort?
  priv tool_contributions_ : ToolContributions?
}

///|
/// Present only when the extension declared `Capability::Model`. The value is
/// source-agnostic: an extension cannot tell a plain provider from a routing
/// meta-extension, and must not.
pub fn CompositionView::model(self : CompositionView) -> &ModelPort? {
  self.model_
}

///|
/// Visible only when the extension declared `Capability::Decision`. Absence is
/// legal: consumers that treat decision support as an enhancement keep their
/// deterministic fallback, and consumers that require it may fail in
/// `on_compose`.
pub fn CompositionView::decision(self : CompositionView) -> &DecisionPort? {
  self.decision_
}

///|
/// Visible only when the extension declared `Capability::Log`. Absence is
/// legal: consumers decide whether a missing sink is acceptable.
pub fn CompositionView::log(self : CompositionView) -> &LogPort? {
  self.log_
}

///|
/// Present only when the extension declared `Capability::Ui`. Declaring `ui`
/// only to consume the composed UI is not allowed.
pub fn CompositionView::ui(self : CompositionView) -> &UiPort? {
  self.ui_
}

///|
/// Present only when the extension declares `Capability::Tasks`.
pub fn CompositionView::tasks(self : CompositionView) -> Tasks? {
  self.tasks_
}

///|
/// Present only when the extension declares `Capability::Wakeup`. `request`
/// queues one ordinary turn for a session; execution stays inside the Agent's
/// single-operation lifecycle.
pub fn CompositionView::wakeup(self : CompositionView) -> WakeupPort? {
  self.wakeup_
}

///|
/// Present only when the extension declares `Capability::ToolContribution`.
/// The value is bound to this extension's manifest id, so it can only publish
/// its own group.
pub fn CompositionView::tool_contributions(
  self : CompositionView,
) -> ToolContributions? {
  self.tool_contributions_
}

///|
/// 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,
  decision~ : &DecisionPort? = None,
  log~ : &LogPort? = None,
  ui~ : &UiPort,
  tasks~ : Tasks? = None,
  wakeup~ : WakeupPort? = None,
  tool_contributions~ : ToolContributions? = None,
) -> CompositionView {
  {
    model_: if requires.contains(Model) {
      Some(model)
    } else {
      None
    },
    decision_: if requires.contains(Decision) {
      decision
    } else {
      None
    },
    log_: if requires.contains(Log) {
      log
    } else {
      None
    },
    ui_: if requires.contains(Ui) {
      Some(ui)
    } else {
      None
    },
    tasks_: if requires.contains(Tasks) {
      tasks
    } else {
      None
    },
    wakeup_: if requires.contains(Wakeup) {
      wakeup
    } else {
      None
    },
    tool_contributions_: if requires.contains(ToolContribution) {
      tool_contributions
    } else {
      None
    },
  }
}