# `PtcRunner.SubAgent.Loop.NativePreview`
[🔗](https://github.com/andreasronge/ptc_runner/blob/main/lib/ptc_runner/sub_agent/loop/native_preview.ex#L1)

Build LLM-facing native tool result previews for combined-mode agents.

Tier 2b of `Plans/text-mode-ptc-compute-tool.md`. When a tool is
configured `expose: :both, cache: true`, the runtime stores the full
result in `state.tool_cache` and returns a *preview* to the LLM. This
module is the pure builder for that preview map.

Three preview shapes are supported, selected by the tool's
`native_result:` option:

  * `preview: :metadata` (default / `nil`) — schema + sample keys + count.
  * `preview: :rows`     — verbatim row sample capped at `limit:`.
  * `preview: <fun/1>`   — custom builder receiving `full_result` only.

All three shapes are merged with the universal cache fields
(`status: "ok"`, `full_result_cached: true`, `cache_hint: "<...>"`)
before being returned. A custom-preview builder that raises, returns a
non-map, or returns a non-`Jason.encode!`-able value falls back to the
metadata preview and emits a `Logger.warning/1` tagged with the tool
name and failure category.

See the plan doc's "Native Tool Result Preview" → "Default Metadata
Preview — Inference Rules" table for the exact metadata shape; this
module is the canonical implementation of that table.

Used by `PtcRunner.SubAgent.Loop.TextMode` in combined mode.

# `preview_status`

```elixir
@type preview_status() :: :ok | :fallback
```

# `build`

```elixir
@spec build(PtcRunner.Tool.t(), term(), map()) :: {preview_status(), map()}
```

Build a preview map for `full_result` per `tool.native_result`.

Returns `{:ok, preview}` for the default-success path or
`{:fallback, preview}` when a custom preview builder failed and the
metadata preview was used as a recovery. Both shapes are
Jason-encodable and carry the universal cache fields
(`status`, `full_result_cached`, `cache_hint`).

`args` is the *canonical* args map (post-`KeyNormalizer` normalization)
used to render the `cache_hint`'s `(tool/<name> <ARGS_FRAGMENT>)`
example. Pass the raw args; the helper canonicalizes internally.

# `cache_hint`

```elixir
@spec cache_hint(String.t(), map()) :: String.t()
```

Render the `cache_hint` string for a native preview.

Format:

    Call lisp_eval and then call (tool/<name> <ARGS>) to
    process the full cached result.

`<ARGS>` is produced by `PtcRunner.Lisp.Formatter.format/1` after
converting the canonical args map to PTC-Lisp AST per the conversion
table in Addendum #26 of the plan. Strings, nested maps, vectors, and
keyword keys all round-trip through Formatter — no hand-rolled
escaping in this module.

---

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