# `PtcRunner.Lisp.Prelude.Attach`
[🔗](https://github.com/andreasronge/ptc_runner/blob/main/lib/ptc_runner/lisp/prelude/attach.ex#L1)

Attach-time validation for a compiled deployment prelude (Capability
Prelude V1, plan §3 / §6A).

Prelude validation is split in two phases:

  * **compile-time** (`PtcRunner.Lisp.Prelude.Compiler`) checks source
    syntax, `(ns ...)` directives, reserved-namespace declarations,
    duplicate refs, visibility, and arity/signature metadata — facts that
    do NOT depend on a selected runtime. It also infers each export's
    `requires` backing ids from literal `(tool/call {:server "x" :tool
    "y" ...})` patterns. Dynamic server/tool values yield `:unknown` effect
    and no requires.

  * **attach-time** (this module) checks each public export's `requires`
    against the SELECTED upstream runtime BEFORE user code is analyzed. If a
    public export requires an upstream operation that is not configured or
    not present on the selected runtime, attachment fails — naming the
    missing operation — so a run never starts against a prelude whose
    backing operations are unavailable.

This split lets a single compiled prelude artifact be reused across runs
while still failing fast when the selected runtime cannot back it.

## Where the attach hook lives

`attach/2` is the seam P2 wires at the TOP of `PtcRunner.Lisp.run` (and the
SubAgent / REPL surfaces), before parsing/analyzing user code. The `prelude:`
option may be either a compiled `%PtcRunner.Lisp.Prelude{}` artifact or raw
prelude source (a binary), which `attach/2` compiles first. On success it
yields the compiled artifact for the analyzer/evaluator path; on failure it
yields a `%PtcRunner.Lisp.Prelude.ValidationError{}` that the call site maps
to `{:error, %PtcRunner.Step{fail: %{reason: :prelude_attach_failed}}}`.

Genuine programmer misuse (passing a value that is neither a prelude
artifact nor source) raises `ArgumentError`; a missing/ungranted upstream op
is a recoverable `{:error, ...}`, never a raise.

## Requires id format

Two backing id shapes are recognized:

  * `"upstream:<server>/<tool>"` — validated against the selected upstream
    runtime. `<server>` must be a configured upstream; `<tool>` must be
    present in that upstream's tool list (mirroring the upstream runtime's
    configured-tool checks). When an upstream's
    tool list is not yet materialized (lazy MCP transports report `nil`
    tools), the specific tool cannot be checked and the requirement passes on
    the configured server alone. When **no** upstream runtime is configured
    for the run, upstream requirements are *skipped* (there is no runtime to
    check; the granted `(tool/call ...)` closure plus `check_undefined_tools`
    still guard the actual surface) — preserving direct-`Lisp.run`-with-stub
    use.

  * `"tool:<name>"` — validated against the run's granted `tools:` map (a
    host-bound typed-tool capability). Fails closed when the host did not
    grant a tool of that name. This is checked regardless of whether an
    upstream runtime is configured.

Any other `requires` id shape fails attachment (fail-closed).

# `runtime`

```elixir
@type runtime() :: struct() | pid() | atom() | nil
```

A selected upstream runtime handle (a `%PtcRunner.Upstream.Runtime{}`
struct, a pid, or a registered name), or `nil` when no upstream runtime is
configured for the run. Mirrors the handle shapes
`PtcRunner.Upstream.Runtime.upstream/2` accepts.

# `attach`

```elixir
@spec attach(
  PtcRunner.Lisp.Prelude.t()
  | String.t()
  | [PtcRunner.Lisp.Prelude.Bundle.selection()],
  PtcRunner.Lisp.Prelude.AttachContext.t()
) ::
  {:ok, PtcRunner.Lisp.Prelude.t()}
  | {:error, PtcRunner.Lisp.Prelude.ValidationError.t()}
```

Resolves `prelude_or_source` to a compiled artifact, then validates its
`requires` against the attach `context` (`%PtcRunner.Lisp.Prelude.AttachContext{}`,
bundling the upstream runtime and the granted `tools:` map).

Returns `{:ok, %PtcRunner.Lisp.Prelude{}}` on success.

Returns `{:error, %ValidationError{}}` when:

  * the source fails compile-time validation (any compile reason), or
  * attach-time `requires` validation fails (`:prelude_attach_failed`).

Raises `ArgumentError` for genuine programmer misuse: a value that is neither
a `%PtcRunner.Lisp.Prelude{}`, prelude source (binary), nor a list of
source-bearing prelude selection maps.

# `validate_requires`

```elixir
@spec validate_requires(
  PtcRunner.Lisp.Prelude.t(),
  PtcRunner.Lisp.Prelude.AttachContext.t()
) ::
  :ok | {:error, PtcRunner.Lisp.Prelude.ValidationError.t()}
```

Validates every public export's `requires` against the attach `context`.

Returns `:ok` when all required backing operations are provided (or when
there are no `requires` to check — e.g. dynamic-backed exports). Returns
`{:error, %ValidationError{reason: :prelude_attach_failed}}` naming the first
missing operation and the export that needs it.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
