# Stack

`Spectre.Stack` compiles the packages and services available to an Agent into
one immutable environment. It replaces implicit global extension discovery
with explicit installation and logical references.

For release and umbrella integration tests, pass the complete package set to
`Spectre.Stack.Conformance.run/2`. It invokes the same compiler invariants as
an Agent Stack and catches compatibility, dependency, conflict, ordering, and
ownership failures that isolated manifest checks cannot see. See
[Foundation Conformance](FOUNDATION_CONFORMANCE.md).

## Define a Stack

```elixir
defmodule MyApp.AI do
  use Spectre.Stack

  install MyApp.Inference do
    provider :openrouter, MyApp.OpenRouter
    model :fast, id: "small-model"
  end

  install MyApp.JobActions
end
```

Package blocks are kept as raw AST until the package compiler receives them.
`provider` and `model` in this example are local forms understood by
`MyApp.Inference`; Stack does not import those names as macros.

Bind the resolved Stack to an Agent:

```elixir
defmodule MyApp.Agent do
  use Spectre.Agent, stack: MyApp.AI
end
```

The compiled Agent stores the Stack module and immutable, digest-fenced
installation references. For each package that declares `agent_extensions`,
the Agent automatically registers the corresponding adapter with that
package's compiled Stack configuration. A package can therefore activate its
selector, memory adapter, turn handler, action provider, flow handler, or
effect executor without asking the application to repeat `use Package`.

It does not copy runtime clients, credentials, processes, or connections into
the Agent definition.

## Installed Does Not Mean Authorized

Installation answers which capabilities exist in the environment. It does not
make every Operation or Action visible to a model, Flow, Work, or Skill.
Binding and policy remain separate:

- package manifests declare capabilities;
- Stack validates ownership and returns closed `Spectre.Stack.Ref` values;
- package-declared Agent extensions activate the installed implementation;
- a later Flow, Work, Skill, or policy binding selects which Refs are usable;
- policy and the host still decide whether a staged effect may execute.

An installed package may intentionally contribute an Action provider, but that
does not make every operation planner-visible or bypass protection and host
authorization.

## Installable Contract V1

A package implements `Spectre.Stack.Installable`:

```elixir
defmodule MyApp.Inference do
  use Spectre.Stack.Installable,
    id: :inference,
    version: "0.3.4",
    contract: 1,
    spectre: "~> 0.3.4",
    provides: [{:service, :inference}],
    operations: [{:inference, :complete}],
    resources: [:client],
    agent_extensions: [MyApp.Inference.Extension],
    dsl: __MODULE__

  alias Spectre.Stack.DSL

  @impl Spectre.Stack.Installable
  def compile(opts, block, caller) do
    declarations = DSL.compile!(block, caller, provider: 2, model: 2)
    {:ok, %{options: opts, declarations: declarations}}
  end
end
```

The manifest fields are:

- `id` and semantic `version`;
- Stack contract version and compatible Spectre requirement;
- provided contracts or uniquely owned services;
- required packages, contracts, or services;
- conflicts;
- namespaced Operation and canonical `{provider, action}` refs;
- runtime resource identifiers;
- Agent extensions that adapt the installation into executable Agent behavior;
- the optional package-local DSL module and portable metadata.

`compile/3` is optional. Its result must be a map or keyword list containing
only immutable data. PID, ports, references, and functions are rejected.
Package manifests and compiled installations receive deterministic SHA-256
digests.

Satellite package tests can execute the versioned contract directly:

```elixir
assert {:ok, package} =
         Spectre.Stack.Contract.V1.verify_installable(MyApp.Inference)
```

## Dependencies And Resolution

Package dependencies use closed descriptors:

```elixir
requires: [
  {:package, :inference, "~> 0.1.5"},
  {:service, :continuity},
  {:contract, :memory_store}
]
```

Stack validates missing and incompatible dependencies, cycles, conflicts,
duplicate packages, and duplicate ownership. Declaration order does not need
to be dependency order.

Resolve capabilities without obtaining their runtime handle:

```elixir
{:ok, ref} =
  Spectre.Stack.resolve(MyApp.AI, :operation, {:inference, :complete})

ref.package
ref.version
ref.stack_digest
```

Supported Ref kinds are `:package`, `:contract`, `:service`, `:operation`,
`:action`, and `:resource`.

## Caller-Owned Runtime

An installable may optionally return supervised resources:

```elixir
@impl Spectre.Stack.Installable
def child_specs(_installation, runtime_opts) do
  client_opts = Keyword.fetch!(runtime_opts, :client)
  [{:client, {MyApp.Client, client_opts}}]
end
```

Start a runtime explicitly and pass secrets only at that boundary. For a
long-lived Instance integration, supervise it under a stable atom or `:via`
name:

```elixir
children = [
  {Spectre.Stack.Runtime,
   stack: MyApp.AI,
   name: MyApp.AIRuntime,
   packages: [inference: [client: runtime_client_options()]]}
]

{:ok, ref} = Spectre.Stack.resolve(MyApp.AI, :resource, :client)
{:ok, client_pid} = Spectre.Stack.Runtime.resolve(MyApp.AIRuntime, ref)
```

The runtime has no default registered name. Resource resolution is read-through
cached; a supervisor child scan occurs only on a cache miss. Runtime and
resource monitors invalidate stale entries, so restarting the named runtime or
one resource resolves the replacement process rather than retaining an old PID.
Child identifiers are fenced by the Stack, Installation, package version, and
digests, so stale Refs do not resolve.

An Instance accepts the runtime address as a top-level runtime-only option:

```elixir
{:ok, instance} =
  Spectre.instance(MyApp.Agent, subject,
    stack_runtime: MyApp.AIRuntime
  )
```

Only an atom name or `{:via, Registry, ...}` reference is accepted. PID, port,
and reference handles are rejected. The historical nested
`opts: [stack_runtime: ...]` form remains compatible, but the address is removed
from base options and never enters Run metadata, checkpoints, Journal records,
receipts, or Definitions.

## Journal identity

When a Journal recorder is configured, routing, runtime, persistence, and
extension records include the authoritative Stack id, owner, digest, and the
id/version/digest of every installation. Package configuration, credentials,
PIDs, connections, and runtime handles are never copied into Journal metadata.

## Legacy Adapters

`Spectre.Stack.Installation.from_extension_mount/1` and
`from_provider_mount/1` materialize legacy mounts as explicit immutable
installations. This keeps existing Extension, provider, and Kinetic integrations
source-compatible while packages adopt the V1 manifest.
